C# HttpClient在Powershell中运行时的并发行为与在Visual Studio中运行时的并发行为不同
我正在使用MS Graph API将数百万用户从on-prem AD迁移到Azure AD B2C,以创建B2C中的用户。我已经编写了一个.Net Core 3.1控制台应用程序来执行此迁移。为了加快速度,我同时调用了Graph API。这是一种很好的工作方式 在开发过程中,我在Visual Studio 2019中运行时体验到了可接受的性能,但对于测试,我是在Powershell 7中的命令行中运行的。从Powershell到HttpClient的并发调用的性能非常差。从Powershell运行时,HttpClient允许的并发调用数量似乎有限制,因此并发批处理中超过40到50个请求的调用开始堆积。它似乎正在运行40到50个并发请求,同时阻止其余的请求 我不是在寻找异步编程方面的帮助。我正在寻找一种解决VisualStudio运行时行为和Powershell命令行运行时行为之间差异的方法。从VisualStudio的绿色箭头按钮在发布模式下运行时的行为与预期一致。从命令行运行不会导致错误 我用异步调用填充任务列表,然后等待task.WhenAll(任务)。每次通话需要300到400毫秒。从VisualStudio运行时,它会按预期工作。我同时批处理1000个调用,每个调用都在预期时间内完成。整个任务块只比最长的单个调用长几毫秒 当我从Powershell命令行运行相同的生成时,行为会发生变化。最初的40到50次呼叫预计需要300到400毫秒,但随后每次呼叫的时间增加到20秒。我认为这些调用是序列化的,所以在其他调用等待时,一次只执行40到50个调用 经过数小时的尝试和错误,我能够将范围缩小到HttpClient。为了隔离问题,我用一个执行Task.Delay(300)并返回模拟结果的方法模拟了对HttpClient.sendaync的调用。在这种情况下,从控制台运行与从VisualStudio运行的行为相同 我正在使用IHttpClientFactory,甚至尝试调整ServicePointManager上的连接限制 这是我的注册码C# HttpClient在Powershell中运行时的并发行为与在Visual Studio中运行时的并发行为不同,c#,visual-studio,powershell,.net-core,httpclient,C#,Visual Studio,Powershell,.net Core,Httpclient,我正在使用MS Graph API将数百万用户从on-prem AD迁移到Azure AD B2C,以创建B2C中的用户。我已经编写了一个.Net Core 3.1控制台应用程序来执行此迁移。为了加快速度,我同时调用了Graph API。这是一种很好的工作方式 在开发过程中,我在Visual Studio 2019中运行时体验到了可接受的性能,但对于测试,我是在Powershell 7中的命令行中运行的。从Powershell到HttpClient的并发调用的性能非常差。从Powershell运
public static IServiceCollection RegisterHttpClient(this IServiceCollection services, int batchSize)
{
ServicePointManager.DefaultConnectionLimit = batchSize;
ServicePointManager.MaxServicePoints = batchSize;
ServicePointManager.SetTcpKeepAlive(true, 1000, 5000);
services.AddHttpClient(MSGraphRequestManager.HttpClientName, c =>
{
c.Timeout = TimeSpan.FromSeconds(360);
c.DefaultRequestHeaders.Add("User-Agent", "xxxxxxxxxxxx");
})
.ConfigurePrimaryHttpMessageHandler(() => new DefaultHttpClientHandler(batchSize));
return services;
}
这是默认的httpclienthandler
internal class DefaultHttpClientHandler : HttpClientHandler
{
public DefaultHttpClientHandler(int maxConnections)
{
this.MaxConnectionsPerServer = maxConnections;
this.UseProxy = false;
this.AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate;
}
}
下面是设置任务的代码
var timer = Stopwatch.StartNew();
var tasks = new Task<(UpsertUserResult, TimeSpan)>[users.Length];
for (var i = 0; i < users.Length; ++i)
{
tasks[i] = this.CreateUserAsync(users[i]);
}
var results = await Task.WhenAll(tasks);
timer.Stop();
以下是使用500个并发请求通过GraphAPI创建的10k B2C用户的指标。前500个请求比正常请求长,因为正在创建TCP连接
这里有一个链接到
这里有一个链接到
VS run度量中的阻塞时间与我在本文中所说的不同,因为我将所有同步文件访问移到了流程的末尾,以尽可能隔离测试运行中出现问题的代码
该项目是使用.NETCore3.1编译的。我正在使用Visual Studio 2019 16.4.5。我想到了两件事。大多数microsoft powershell都是在版本1和2中编写的。版本1和2具有MTA的System.Threading.Thread.ApartmentState。在版本3到版本5中,单元状态默认更改为STA 第二个想法是,听起来他们好像在使用System.Threading.ThreadPool来管理线程。你的线程池有多大 如果这些都不能解决问题,开始在System.Threading下挖掘 当我读到你的问题时,我想到了这个博客 一位同事演示了一个示例程序,该程序创建了1000个工作项,每个工作项都模拟一个需要500毫秒才能完成的网络调用。在第一个演示中,网络调用阻塞了同步调用,示例程序将线程池限制为10个线程,以使效果更加明显。在这种配置下,前几个工作项很快被分派到线程,但随后延迟开始增加,因为没有更多的线程可用于服务新的工作项,因此剩余的工作项必须等待越来越长的时间,线程才能用于服务新的工作项。到工作项开始的平均延迟超过两分钟 更新1: 我从“开始”菜单运行PowerShell 7.0,线程状态为STA。两个版本中的线程状态是否不同
PS C:\Program Files\PowerShell\7> [System.Threading.Thread]::CurrentThread
ManagedThreadId : 12
IsAlive : True
IsBackground : False
IsThreadPoolThread : False
Priority : Normal
ThreadState : Running
CurrentCulture : en-US
CurrentUICulture : en-US
ExecutionContext : System.Threading.ExecutionContext
Name : Pipeline Execution Thread
ApartmentState : STA
更新2:
我希望得到更好的答案,但是,您必须对这两种环境进行比较,直到有东西脱颖而出
PS C:\Windows\system32> [System.Net.ServicePointManager].GetProperties() | select name
Name
----
SecurityProtocol
MaxServicePoints
DefaultConnectionLimit
MaxServicePointIdleTime
UseNagleAlgorithm
Expect100Continue
EnableDnsRoundRobin
DnsRefreshTimeout
CertificatePolicy
ServerCertificateValidationCallback
ReusePort
CheckCertificateRevocationList
EncryptionPolicy
更新3:
此外,每个HttpClient实例都使用自己的连接池,
将其请求与其他HttpClient执行的请求隔离
实例
如果应用程序在Windows.Web.Http中使用HttpClient和相关类
命名空间下载大量数据(50 MB或更多),然后
应用程序应该以流的方式下载这些内容,而不是使用默认值
缓冲。如果使用默认缓冲,则客户端内存使用率
将变得非常大,可能导致性能降低
只要不断比较这两种环境,问题就会突出
Add-Type -AssemblyName System.Net.Http
$client = New-Object -TypeName System.Net.Http.Httpclient
$client | format-list *
DefaultRequestHeaders : {}
BaseAddress :
Timeout : 00:01:40
MaxResponseContentBufferSize : 2147483647
在第一批测试之后,您是否检查了与netstat实用程序的连接状态?它可能会提供一些关于前几个任务完成后发生的情况的见解。如果您不以这种方式解决它(异步HTTP请求),您可以始终在ConcurrentQueue[object]消费者/生产者并行中为每个用户使用同步HTTP调用。我最近在PowerShell中为大约2亿个文件做了这项工作。@IP3R我只是重新阅读了你的推荐,这次我理解了。我会记住的。不,我是说,如果你想去PowerShell而不是c::。@thepip3r刚刚读了斯蒂芬·克利里的博客。我应该很好。在Powershell 7.0系统中运行时。Threading.Thread.CurrentThread.GetApartmentState()从程序中返回MTA。Main()默认最小线程池为12,我尝试增加最小线程池
Add-Type -AssemblyName System.Net.Http
$client = New-Object -TypeName System.Net.Http.Httpclient
$client | format-list *
DefaultRequestHeaders : {}
BaseAddress :
Timeout : 00:01:40
MaxResponseContentBufferSize : 2147483647