.NET HTTPClient异步限制

8
我有一个小的.Net 4.5 C#应用程序,它从数据源读取信息,然后将这些信息推送到一个.NET 4.5 Web API站点,该站点具有一个简单的控制器。控制器接收数据并将其放入数据库中。
对于我来说,以下内容是有效的,只要应用程序能够读取,它就可以写入,所有内容最终都会进入数据库中:
    public static void PostDataToWebApi(MyDataClass tData)
    {
        HttpResponseMessage s = null;

        try
        {
            s = client.PostAsJsonAsync("/api/Station/Collector", tData).Result;
            s.EnsureSuccessStatusCode();
        }
        catch (Exception e)
        {
            Console.WriteLine("ERROR (ClientPost): " + e.ToString());
        }
    }

以下代码无法正常运行。它会POST大约一千条记录,然后出现多个错误,带有“任务已取消”的消息,但是大约10秒后会恢复处理:
    public static async void PostDataToWebApi(MyDataClass tData)
    {
        HttpResponseMessage s = null;

        try
        {
            s = await client.PostAsJsonAsync("/api/Station/Collector", tData);
            s.EnsureSuccessStatusCode();
        }
        catch (Exception e)
        {
            Console.WriteLine("ERROR (ClientPost): " + e.ToString());
        }
    }

完整的错误信息如下:
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
    at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
    at IICE_DataCollector_Remote.Program.<PostDataToWebApi>d__7.MoveNext() in e:\Users\TestUser.TEST\Documents\Visual Studio 2012\Projects\Test_App-trunk\TestCollector\Program.cs:line 475

有没有快速的解决方案? 据我所知,它会耗尽某些资源,如线程、套接字等,谁知道呢 :-)
如果您有任何建议,将不胜感激。我很想让这个程序正常工作,因为如您所想象的那样,同步进行POST比异步要慢得多。
为了确保这不是我的机器、本地防病毒软件或网络的问题,我在W2k8 R2服务器、Windows 7虚拟桌面(新版本)和Windows 8机器上进行了测试,结果相同。
更多信息: 我已经使用局域网连接测试过较小数据集(10,000条记录),DefaultConnectionLimit为100,取得了部分成功。但是,在生产环境中(500,000条记录),当向跨互联网远程服务器发送请求时(延迟仍然低,25ms-50ms),我并没有取得任何成功。
提前感谢您的任何帮助 :-)

这个异常的信息是什么? - cuongle
你的第二个版本中不是缺少了一个 await 吗?看起来它甚至不能编译... - Jon Skeet
@CuongLe,该消息为“任务已取消”。 - Dominik
@StephenCleary 不,我会查看一些代码示例并尝试一下。你有什么关于设置限制的建议吗? - Dominik
4
@Dominik:默认值为2,这是公共服务器访问的非常不错的设置。由于这是您的服务器,您可以将其设置为您认为可接受的任何值(最好留出一些余地,并记住SQL Server通常不像WebAPI那样具有很高的扩展性)。一旦您实验并找到适当的设置,然后您可以在客户端使用SemaphoreSlim来限制您的请求。 - Stephen Cleary
显示剩余5条评论
1个回答

8

好的,现在它可以正常工作了。最重要的是为服务器端调整客户端设置。这些设置因本地测试或通过互联网进行测试而异。

我的“PostDataToWebApi”方法现在看起来像这样:

    public static async void PostDataToWebApi(MyDataClass tData)
        {

        await throttler.WaitAsync();
        allTasks.Add(Task.Run(async () =>
        {

            try
            {
                var s = await client.PostAsJsonAsync("/api/Station/Collector", tData).ConfigureAwait(false);
            }
            catch (Exception e)
            {
                Console.WriteLine("ERROR (ClientPost): " + e.ToString());
            }
            finally
            {
                throttler.Release();
            }
        }));
    }

我在控制台应用程序的顶部声明了以下内容:

    private static List<Task> allTasks = new List<Task>();
    private static SemaphoreSlim throttler;

在我的循环开始之前,我有以下内容,变量已更改以确保所有内容都可以正常工作:
    ServicePointManager.DefaultConnectionLimit = _DefaultConnections;
    ServicePointManager.MaxServicePointIdleTime = _MaxIdleTime;
    ServicePointManager.Expect100Continue = false;
    ServicePointManager.CheckCertificateRevocationList = false;

    throttle = new SemaphoreSlim(initialCount: _MaxQueue);

作为指南,对于基于互联网的交易,以下内容对我有用:
  1. 默认连接数:24
  2. 最大空闲时间:400
  3. SemaphoreSlim初始计数:50
对于我的局域网测试,我可以将默认连接和初始计数值提高,没有出现任何问题,这是可以预料的 :-)
最后,在我的代码外部,我使用以下内容来确保在执行结束时不会终止任何正在运行的任务:
     await Task.WhenAll(allTasks);

希望这能帮到你!

感谢您的自我回答!这似乎是违反直觉的,但我发现将信号量的初始计数设置得比连接计数更高非常重要,就像示例设置一样。否则,吞吐量会大幅降低。 - stannius

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接