3000+的瓶颈;线程.NET应用程序(HttpWebReqsuest.BeginGetResponse)

3000+的瓶颈;线程.NET应用程序(HttpWebReqsuest.BeginGetResponse),.net,vb.net,multithreading,.net,Vb.net,Multithreading,因此,我正在创建一个应用程序,它使用HTTP API获取大约50000个帐户中每个帐户的状态。代码循环遍历每个帐户,并为其发送HTTP请求。当我收到http请求的响应时,基于某些条件,我创建一个新线程来管理该帐户 现在,通常情况下,我可以轻松地每秒发送约1K个请求,但一旦我达到3100个活动线程,http请求循环就会开始减速,并冻结到每秒仅发送约1个请求。然后它突然跳回到每秒3公里,然后冻结几秒钟……等等。重要的是,它看起来不像是一个逐渐退化的过程。事情发生得很突然 显然,某个地方存在瓶颈,但我

因此,我正在创建一个应用程序,它使用HTTP API获取大约50000个帐户中每个帐户的状态。代码循环遍历每个帐户,并为其发送HTTP请求。当我收到http请求的响应时,基于某些条件,我创建一个新线程来管理该帐户

现在,通常情况下,我可以轻松地每秒发送约1K个请求,但一旦我达到3100个活动线程,http请求循环就会开始减速,并冻结到每秒仅发送约1个请求。然后它突然跳回到每秒3公里,然后冻结几秒钟……等等。重要的是,它看起来不像是一个逐渐退化的过程。事情发生得很突然

显然,某个地方存在瓶颈,但我不确定在哪里。我已经确保TCP参数(可用端口的最大数量)设置为上限。我已将servicepoint.defaultconnection限制设置为int.maxvalue

我的CPU是一个4核(8线程,带HT)的专用1Gbps处理器。我正在考虑换一台更大(32核2xCPU)的机器。但我不确定它是否会有任何好处。我想知道是否有人知道我可能遇到的其他瓶颈

我甚至还没有用完我所有的带宽或内存,我知道这不是一个问题

这大概就是我的代码的样子

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个这样的建议怎么样?