我有这种情况,我正在研究需要将通知消息发送到我公司所有工作站的地方。我们有一个处理桌面通知的客户端服务,它是一个非常简单的WebApi。
问题/要求 我如何异步和并行向server / ASP.Net Web应用程序发送所有这些机器的请求,并捕获其对自定义日志文件的响应?
在发出通知时可能会关闭许多机器,或者由于机器可能已经退役,因此dns可能无法解决。
当前正在构建原型的代码的请求/响应周期需要非阻塞代码。这样用户就不必等待所有这些机器的响应。
我的家庭作业: 我理解异步操作更适合IO绑定操作,但是机器数量太多使我感觉并行和异步 在一起,可能更适合这种情况。
我已设置ServicePointManager.DefaultConnectionLimit = 10000
已经传递了带有dispose选项为false的构造函数的HttpClient。见下面的代码 - 工厂类。
框架中的HttpClient类是为重用而设计的,但据我所知,有些属性不像基地址那样被更改。资料来源:HttpClient - This instance has already started
我正在使用请求/响应特定ID(相关ID)来确保对给定请求的所有异步和并行操作都在同一文件夹中。
TaskCanceledException catch块永远不会被命中!!
超时已延长至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);
}
}
}
}
IHttpClientFactory是Transient(IoC)
workstationUrls.ForEach(baseUrl =>
{
_httpClientFactory.CreateClient(baseUrl, async (client) =>
{
await client.PostAsync(resourceUrl, content).ContinueWith(t =>
{
try
{
var response = t.Result;
var workstationResponse = new WorkstationResponse
{
StatusCode = (int)response.StatusCode,
Response = response.Content.ReadAsStringAsync().Result
};
workstationResponse.IsSuccess
= workstationResponse.StatusCode >= 200 &&
workstationResponse.StatusCode <= 299
? true
: false;
var docContent = JsonConvert.SerializeObject(workstationResponse);
if (workstationResponse.IsSuccess)
{
// Write workstation log
File.WriteAllText(path
+ "\\Bulk Notifications\\"
+ userFolder + "\\Success\\"
+ GetWorkstationNameFromUrl(baseUrl)
+ GetUniqueTimeStampForFileNames()
+ workstationResponse.IsSuccess + ".txt",
docContent);
}
else
{
// Write workstation log
File.WriteAllText(path
+ "\\Bulk Notifications\\"
+ userFolder + "\\Fail\\"
+ GetWorkstationNameFromUrl(baseUrl)
+ GetUniqueTimeStampForFileNames()
+ workstationResponse.IsSuccess + ".txt",
docContent);
}
}
catch (TaskCanceledException exe)
{
}
catch (Exception ex)
{
var workstationResponse = new WorkstationResponse
{
Exception = ex,
IsSuccess = false
};
var docContent = JsonConvert.SerializeObject(workstationResponse);
// Write workstation log
File.WriteAllText(path
+ "\\Bulk Notifications\\"
+ userFolder + "\\Fail\\"
+ GetWorkstationNameFromUrl(baseUrl) + " "
+ GetUniqueTimeStampForFileNames() + " "
+ workstationResponse.IsSuccess + ".txt", docContent);
}
});
});
});
原型代码问题: 我在Exception消息中得到这些错误,而StackTrace似乎没有帮助
答案 0 :(得分:3)
首先,异步和并行不是互斥的;不同之处在于您使用的构造以及与传统&#34;不同的事实。并行性,你不会为每个异步操作消耗/阻塞一个线程。
同时执行异步操作并等待(异步)完成所有这些操作的最基本构造是Task.WhenAll
。我首先定义了一个异步方法,用于向单个工作站发送消息:
async Task SendMessageToWorkstationAsync(string url)
(可以从代码中轻松派生实现。)
然后这样称呼:
await Task.WhenAll(workstationUrls.Select(SendMessageToWorkstationAsync));
其次,关于HttpClient
,如果由于您正在设置BaseAddress
而未重复使用单个实例,则解决方案很简单:不要这样做。 :)它不是必需的。跳过工厂类,只需创建一个共享的HttpClient
实例,并为每个请求提供完整的URI。
最后,根据各种因素,您可能仍会发现62,000多个并发请求比系统可以处理的多。如果是这种情况,您会想要限制并行性。我发现最好的方法是使用TPL Dataflow。有关如何执行此操作的详细信息,请参阅this question。