您可能会在下面的代码中看到,在较高的层次上,该代码以递归方式读取文件夹结构,并将其内容发布到API。应用程序是.Net core 2.1。
我有这项服务,可以对API进行POST。
public class EnterpriseService
{
private readonly HttpClient _httpClient;
public EnterpriseService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<string> PostTransactionAsync(byte[] payload)
{
using (var request = new HttpRequestMessage(HttpMethod.Post, new Uri("https://www.foo.com/api/transaction")))
{
request.Content = new ByteArrayContent(payload);
HttpResponseMessage response = await _httpClient.SendAsync(request);
return await response.Content.ReadAsStringAsync();
}
}
}
PostTransactionAsync通过以下方式被调用方调用:
protected async Task SearchFoldersAsync(List<FileStatusProperties> folders, string root, CancellationToken cancellationToken)
{
await Task.Run(() =>
{
return Parallel.ForEach(folders, async entry =>
{
if (entry.Type == FileType.DIRECTORY)
{
await SearchFoldersAsync(
DataLakeStorage.DirectoryGetFiles($"{root}/{entry.PathSuffix}"),
$"{root}/{entry.PathSuffix}", cancellationToken);
return;
}
byte[] payload = DataLakeStorage.FileDownload($"{root}/{entry.PathSuffix}");
await _enterpriseService.PostTransactionAsync(payload);
});
}, cancellationToken);
}
请注意,我使用的是直接作为单例的HttpClient。
我还递归使用Parallel.Foreach。
此代码非常适合包含10K +文件的较小文件夹结构。但是,当文件数量增加时(例如,文件夹中的文件数量达到了约10万个),我会混合使用这2个错误。大约20%的请求成功。 _httpClient.SendAsync调用中的40%的请求最终都遇到这两个异常。请求在10秒后失败。
每个套接字地址(协议/网络地址/端口)仅一种用法 通常是允许的
和
操作被取消。无法从传输中读取数据 连接:由于以下原因之一,I / O操作已被中止: 线程退出或应用程序请求。 I / O操作已 由于线程退出或应用程序请求而中止
我了解了HttpClient的用法,据我所知,我没有做错任何事情。但是我不确定它是否与递归Parallel.ForEach一起使用。
我想知道在需要同时进行大量http请求的情况下处理此情况的推荐方法是什么?
答案 0 :(得分:1)
Parallel
用于并行性,它是一种并发形式,它使用多个线程在多个内核之间分配CPU限制的工作。您想要的是异步并发,这是同时执行多个I / O绑定操作的更合适的方法。
最容易完成异步并发的方法是,为每个项目启动Task
(通常使用Select
),然后对所有这些任务执行await Task.WhenAll
。像这样:
protected async Task SearchFoldersAsync(List<FileStatusProperties> folders, string root, CancellationToken cancellationToken)
{
var tasks = folders.Select(async entry =>
{
if (entry.Type == FileType.DIRECTORY)
{
await SearchFoldersAsync(
DataLakeStorage.DirectoryGetFiles($"{root}/{entry.PathSuffix}"),
$"{root}/{entry.PathSuffix}", cancellationToken);
return;
}
byte[] payload = DataLakeStorage.FileDownload($"{root}/{entry.PathSuffix}");
await _enterpriseService.PostTransactionAsync(payload);
}).ToList();
await Task.WhenAll(tasks);
}
答案 1 :(得分:0)
我不会处理Parallel vs async ...
但是这个特定的错误
每个套接字地址(协议/网络地址/端口)通常只允许使用一种
似乎是由于从一个系统到另一个系统上的单个端口只能有大约65k的连接。
假设现有服务器进程正在使用端口80,则可以启动使用其他端口的其他进程。但是,您需要多个1 HttpClient,并且需要在它们之间进行轮询或其他操作。进程太多,您可能开始达到客户端或服务器上打开文件描述符的限制。