C# 异步和并行HttpClient请求到62000+;机器:最佳实践请求

C# 异步和并行HttpClient请求到62000+;机器:最佳实践请求,c#,asp.net,asp.net-web-api,httpclient,dotnet-httpclient,C#,Asp.net,Asp.net Web Api,Httpclient,Dotnet Httpclient,我有这个场景,我正在研究需要将通知消息发送到公司所有工作站的位置。我们有一个处理桌面通知的客户端服务,它是一个非常简单的WebApi 问题/要求 如何从server/ASP.Net web app异步和并行地向所有这些计算机发送请求,并捕获其对自定义日志文件的响应 许多机器可能在发出通知时关闭,或者dns可能无法解析,因为机器可能已停用 当前原型化代码的请求/响应周期需要为非阻塞周期。这样用户就不必等待所有这些机器的响应 我的作业: 我知道异步操作更适合于IO绑定的操作,但是机器的数量太多,让我

我有这个场景,我正在研究需要将通知消息发送到公司所有工作站的位置。我们有一个处理桌面通知的客户端服务,它是一个非常简单的WebApi

问题/要求 如何从server/ASP.Net web app异步和并行地向所有这些计算机发送请求,并捕获其对自定义日志文件的响应

许多机器可能在发出通知时关闭,或者dns可能无法解析,因为机器可能已停用

当前原型化代码的请求/响应周期需要为非阻塞周期。这样用户就不必等待所有这些机器的响应

我的作业: 我知道异步操作更适合于IO绑定的操作,但是机器的数量太多,让我感觉并行和异步 加在一起,可能更适合这种情况

我已将ServicePointManager.DefaultConnectionLimit设置为10000

已传递构造函数的HttpClient,其dispose选项为false。请参阅下面的代码-工厂类

框架中的HttpClient类是为重用而设计的,但据我所知,某些属性不能像基址那样更改。资料来源:

我使用特定于请求/响应的ID(关联ID)来确保给定请求的所有异步和并行操作都位于同一文件夹中

TaskCanceledException捕获块永远不会被命中

超时时间已经延长到30秒,我确信这已经足够了

原型代码:

public class HttpClientFactory  : IHttpClientFactory
    {
        public void CreateClient(string baseUrl, Action<HttpClient> methodToExecute)
        {
            using (var handler = new HttpClientHandler())
            {
                handler.AllowAutoRedirect = true;
                using (var client = new HttpClient(handler, false))
                {
                    client.BaseAddress = new Uri(baseUrl);
                    client.Timeout = TimeSpan.FromSeconds(30);
                    methodToExecute(client);
                }
            }
        }
   }
公共类HttpClientFactory:IHttpClient工厂
{
public void CreateClient(字符串baseUrl,操作方法执行)
{
使用(var handler=new-HttpClientHandler())
{
handler.AllowAutoRedirect=true;
使用(var client=newhttpclient(handler,false))
{
client.BaseAddress=新Uri(baseUrl);
client.Timeout=TimeSpan.FromSeconds(30);
方法执行(客户);
}
}
}
}
其中IHttpClientFactory为瞬态(IoC)

workstationUrls.ForEach(baseUrl=>
{
_httpClientFactory.CreateClient(baseUrl,异步(客户端)=>
{
等待client.PostAsync(资源URL、内容).ContinueWith(t=>
{
尝试
{
var响应=t.结果;
var workstationResponse=新的workstationResponse
{
StatusCode=(int)response.StatusCode,
Response=Response.Content.ReadAsStringAsync().Result
};
workstationResponse.IsSuccess
=workstationResponse.StatusCode>=200&&

workstationResponse.StatusCode首先,异步和并行并不是相互排斥的;区别在于您使用的构造,以及与“传统”并行不同的是,您没有为每个异步操作消耗/阻塞线程

并发执行异步操作并等待(异步)所有操作完成的最基本构造是
Task.whalll

async Task SendMessageToWorkstationAsync(string url)
(可以很容易地从代码中派生实现。)

那么就这样称呼它:

await Task.WhenAll(workstationUrls.Select(SendMessageToWorkstationAsync));
其次,关于
HttpClient
,如果因为设置
BaseAddress
而没有重用单个实例,那么解决方案很简单:不要这样做。:)这不是必需的。跳过工厂类,只需创建一个共享的
HttpClient
实例,并为每个请求提供完整的URI


最后,根据各种因素,您可能仍然会发现62000多个并发请求超出了系统的处理能力。如果是这种情况,您将希望限制并行性。我发现最好的方法是使用TPL数据流。有关如何做到这一点的详细信息,请参阅。

首先,异步和并行并不是相互排斥的不同之处在于您使用的构造,以及与“传统”并行不同的是,每个异步操作不消耗/阻塞线程

并发执行异步操作并等待(异步)所有操作完成的最基本构造是
Task.whalll

async Task SendMessageToWorkstationAsync(string url)
(可以很容易地从代码中派生实现。)

那么就这样称呼它:

await Task.WhenAll(workstationUrls.Select(SendMessageToWorkstationAsync));
其次,关于
HttpClient
,如果因为设置
BaseAddress
而没有重用单个实例,那么解决方案很简单:不要这样做。:)这不是必需的。跳过工厂类,只需创建一个共享的
HttpClient
实例,并为每个请求提供完整的URI


最后,根据各种因素,您可能仍然会发现62000多个并发请求超出了系统的处理能力。如果是这种情况,您将希望限制并行性。我发现最好的方法是使用TPL数据流。有关如何做到这一点的详细信息,请参阅。

从更改操作
操作方法开始将xecute
发送到一个func,该func返回一个任务,以便您可以等待与
HttpClient
正确交互的异步方法。这导致了已处理的异常。Thans Peter,让我试试看这是否有什么不同。从更改