在c#中多线程处理大量Web请求

Ter*_*rry 23 c# multithreading webrequest

我有一个程序,我需要在外部sharepoint站点创建一些大量的文件夹(外部意思是我不能使用sharepoint对象模型).Web请求适用于此,但只需一次执行一个(发送请求,等待响应,重复)相当慢.我决定多线程化请求,尝试加快速度.该程序已大大加快,但经过一段时间(1-2分钟左右)后,并发异常开始被抛出.

代码在下面,这是最好的方法吗?

Semaphore Lock = new Semaphore(10, 10);
List<string> folderPathList = new List<string>();
//folderPathList populated

foreach (string folderPath in folderPathList)
{
    Lock.WaitOne();
    new Thread(delegate()
    {
        WebRequest request = WebRequest.Create(folderPath);
        request.Credentials = DefaultCredentials;
        request.Method = "MKCOL";

        WebResponse response = request.GetResponse();
        response.Close();
        Lock.Release();
    }).Start();
}
for(int i = 1;i <= 10;i++)
{
    Lock.WaitOne();
}
Run Code Online (Sandbox Code Playgroud)

例外情况是这样的

未处理的异常:System.Net.WebException:无法连接到远程服务器---> System.Net.Sockets.SocketException:System.Net.Sockets通常只允许192.0.0.1:81使用每个套接字地址的一个用法
.
System.Net.SService.ConnectSocketInternal上的System.Net.Sockets.Socket.InternalConnect(EndPoint remoteEP)
中的Socket.DoConnect(EndPoint endPointSnapshot,SocketAddre ss socketAddress)(布尔值connectFailure,套接字s4,套接字s6,套接字和套接字,IPAddress和地址,ConnectSocketState state,IAsyncResult asyncResult,Int32 timeout,Exception&exception)

nos*_*nos 21

您可能会创建太多连接,从而耗尽您可以使用的所有本地端口.关闭端口后可以重新使用端口的超时时间. WebRequest隐藏所有低级套接字处理,但我猜它最终用完了端口,或者尝试(重新)绑定到已经处于TIME_WAIT状态的套接字.

即使您不关心响应,也应确保阅读响应流.这应该有助于不产生太多延迟的连接.

WebResponse response = request.GetResponse();
new StreamReader(response.GetResponseStream()).ReadToEnd(); 
Run Code Online (Sandbox Code Playgroud)

我将从这里粘贴一些相关信息:

当连接关闭时,在关闭连接的一侧,5元组{协议,本地IP,本地端口,远程IP,远程端口}默认进入TIME_WAIT状态240秒.在这种情况下,协议是固定的 - TCP本地IP,远程IP和远程PORT通常也是固定的.所以变量是本地端口.发生的情况是,当您不绑定时,使用1024-5000范围内的端口.所以粗略地说你有4000个端口.如果您在4分钟内使用所有这些 - 意味着大约每4秒钟进行16次Web服务呼叫,您将耗尽所有端口.这就是这个例外的原因.

好的,现在怎么解决?

  1. 其中一种方法是增加动态端口范围.默认情况下,最大值为5000.您可以将其设置为65534. HKLM\System\CurrentControlSet\Services\Tcpip\Parameters\MaxUserPort是要使用的密钥.

  2. 您可以做的第二件事是,一旦连接进入TIME_WAIT状态,您可以减少它处于该状态的时间,默认为4分钟,但您可以将此设置为30秒HKLM\System\CurrentControlSet\Services\Tcpip \参数\ TCPTimedWaitDelay是要使用的密钥.将此设置为30秒


Hom*_*mde 10

您没有关闭可能导致连接长时间打开的webrequest.对于Parallel.Net的Parallel.Foreach来说,这听起来是一个完美的工作,只需要确定你希望它运行多少个线程

  ParallelOptions parallelOptions = new ParallelOptions();

        parallelOptions.MaxDegreeOfParallelism = 10;
        Parallel.ForEach(folderPathList, parallelOptions, folderPathList =>
        {
            using(WebRequest request = WebRequest.Create(folderPath))
            {
               request.Credentials = DefaultCredentials;
               request.Method = "MKCOL";

               GetResponse request = WebRequest.Create(folderPath);
               request.Credentials = DefaultCredentials;
               request.Method = "MKCOL";
               using (WebResponse response = request.GetResponse());
            }
        });
Run Code Online (Sandbox Code Playgroud)

要记住的另一件事是maxConnections,请务必在app.config中设置它:

<configuration>
  <system.net>
    <connectionManagement>
      <add address = "*" maxconnection = "100" />
    </connectionManagement>
  </system.net>
</configuration>
Run Code Online (Sandbox Code Playgroud)

在实际场景中,您必须添加try-catch并重试可能超时的连接,从而导致更复杂的代码

  • 太糟糕的WebRequest不是一次性的 (3认同)