3000+的瓶颈;线程.NET应用程序(HttpWebReqsuest.BeginGetResponse)
因此,我正在创建一个应用程序,它使用HTTP API获取大约50000个帐户中每个帐户的状态。代码循环遍历每个帐户,并为其发送HTTP请求。当我收到http请求的响应时,基于某些条件,我创建一个新线程来管理该帐户 现在,通常情况下,我可以轻松地每秒发送约1K个请求,但一旦我达到3100个活动线程,http请求循环就会开始减速,并冻结到每秒仅发送约1个请求。然后它突然跳回到每秒3公里,然后冻结几秒钟……等等。重要的是,它看起来不像是一个逐渐退化的过程。事情发生得很突然 显然,某个地方存在瓶颈,但我不确定在哪里。我已经确保TCP参数(可用端口的最大数量)设置为上限。我已将servicepoint.defaultconnection限制设置为int.maxvalue 我的CPU是一个4核(8线程,带HT)的专用1Gbps处理器。我正在考虑换一台更大(32核2xCPU)的机器。但我不确定它是否会有任何好处。我想知道是否有人知道我可能遇到的其他瓶颈 我甚至还没有用完我所有的带宽或内存,我知道这不是一个问题 这大概就是我的代码的样子3000+的瓶颈;线程.NET应用程序(HttpWebReqsuest.BeginGetResponse),.net,vb.net,multithreading,.net,Vb.net,Multithreading,因此,我正在创建一个应用程序,它使用HTTP API获取大约50000个帐户中每个帐户的状态。代码循环遍历每个帐户,并为其发送HTTP请求。当我收到http请求的响应时,基于某些条件,我创建一个新线程来管理该帐户 现在,通常情况下,我可以轻松地每秒发送约1K个请求,但一旦我达到3100个活动线程,http请求循环就会开始减速,并冻结到每秒仅发送约1个请求。然后它突然跳回到每秒3公里,然后冻结几秒钟……等等。重要的是,它看起来不像是一个逐渐退化的过程。事情发生得很突然 显然,某个地方存在瓶颈,但我
Sub Main()
While 1
For each account As Account in GetAccountsFromDatabase()'~50K Accounts
dim request = HttpWebRequest.Create("http://api.com?id=" & account.name)
request.BeginGetResponse(New AsyncCallback(AddressOf HandleResponse), request)
RequestsSent += 1
Console.Writeline("Request")
'After ~3000 active threads in the process, this loop begins freezing/slowing down.
if RequestsSent > 5000 then 'Limit
Thread.Sleep(5000)
RequestsSent = 0
end if
Next
End While
End Sub
Sub HandleResponse(ByVal asynchronousResult As IAsyncResult)
Dim webRequest As HttpWebRequest = DirectCast(asynchronousResult.AsyncState, HttpWebRequest)
Dim webResponse As HttpWebResponse = webRequest.EndGetResponse(asynchronousResult)
Dim stream As New StreamReader(webResponse.GetResponseStream())
Dim response = stream.ReadToEnd
if response.contains("somestuff") then
dim t As New Thread(AddressOf ProcessAccount)
t.Start(account)
end if
End Sub
Sub ProcessAccount(acc As Account)
'Process the account. Involves some other loops, http requests...etc
End Sub
您正在异步启动请求,但一旦
BeginGetResponse
完成,您将同步处理请求。这最终可能会消耗池中的大量线程。配置中的池限制可能在3000个线程左右
你得把这个扔掉。一旦进入数百个线程,而且肯定是1000个线程,就需要切换到异步非阻塞IO。使用wait
这变得非常简单
使流读取部分异步(ReadToEnd
)。可能,您应该删除所有代码,并将其替换为var str=await new HttpClient().GetAsync(url)代码>(C#)
其次,运行50000(!)个线程是不明智的。仅此一项就可以为堆栈设置50 GB(!)的内存。我在测试中发现,操作系统在调度所有线程时也会遇到问题。例如,尽管鼠标驱动程序具有非常高的优先级,但鼠标会停止移动几秒钟。显然,内核团队并不太关心这个场景
也许,您应该以给定的并行度处理帐户。例如,只能同时处理100个进程
如果出于某种原因需要同时处理它们,那么ProcessAccount
也必须是异步的。没有别的办法。这里有一些伪代码来解释我将如何处理这个问题。您肯定不想触发无限数量的线程。工作线程的数量应该根据您的环境进行调整
WorkQueue = GetAccountsFromDatabase()'~50K Accounts
for 1 to 100
Workers.Add(Task.Run(() => Worker(WorkQueue)))
end
WaitForWorkersToFinish(Workers)
return
Worker(WorkQueue)
{
while 1
lock (WorkQueue)
WorkItem = WorkQueue.Next() // removes item
end
if WorkItem == null
return // out of work
end
ProcessWorkItem(WorkItem)
end
}
您使用什么调试工具?你确定核心是100%吗?我以前从未使用过这个,但看起来很有希望。从没听说过,我现在就去看看。3000个线程?!一定有办法减少这种情况。我试着用你的“等待”建议替换所有的请求代码,但结果使问题变得更糟。循环将在~1K个线程处开始挂起/冻结。然后你没有做足够的异步工作。发生挂起时暂停调试器。有多少条线?你认为你应该并行处理100个这样的建议怎么样?