C# 提高 .NET 中多线程 HttpWebRequests 的性能

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/388908/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 01:34:45  来源:igfitidea点击:

Improving performance of multithreaded HttpWebRequests in .NET

c#.netmultithreadinghttpwebrequest

提问by Rasmus Faber

I am trying to measure the throughput of a webservice.

我正在尝试测量 Web 服务的吞吐量。

In order to do that, I have written a small tool that continuously sends requests and reads responses from a number of threads.

为了做到这一点,我编写了一个小工具,它不断地发送请求并从多个线程读取响应。

The contents of the inner loop of each thread looks like this:

每个线程的内循环内容如下所示:

public void PerformRequest()
{
  WebRequest webRequest = WebRequest.Create(_uri);

  webRequest.ContentType = "application/ocsp-request";
  webRequest.Method = "POST";
  webRequest.Credentials = _credentials;
  webRequest.ContentLength = _request.Length;
  ((HttpWebRequest)webRequest).KeepAlive = false;

  using (Stream st = webRequest.GetRequestStream())
    st.Write(_request, 0, _request.Length);

  using (HttpWebResponse httpWebResponse = (HttpWebResponse)webRequest.GetResponse())
  using (Stream responseStream = httpWebResponse.GetResponseStream())
  using (BufferedStream bufferedStream = new BufferedStream(responseStream))
  using (BinaryReader reader = new BinaryReader(bufferedStream))
  {
    if (httpWebResponse.StatusCode != HttpStatusCode.OK)
      throw new WebException("Got response status code: " + httpWebResponse.StatusCode);

    byte[] response = reader.ReadBytes((int)httpWebResponse.ContentLength);
    httpWebResponse.Close();
  }      
}

It seems to work okay, except that something seems to be limiting the tool. If I run two instances of the tool with each 40 threads, I get significantly more throughput than one instance with 80 threads.

它似乎工作正常,只是似乎有些东西限制了该工具。如果我运行该工具的两个实例,每一个 40 个线程,我获得的吞吐量明显高于一个具有 80 个线程的实例。

I found the ServicePointManager.DefaultConnectionLimit property, which I set to 10000 (and it makes no difference if I set it through app.config as suggested by Jader Dias).

我找到了 ServicePointManager.DefaultConnectionLimit 属性,我将其设置为 10000(如果我按照 Jader Dias 的建议通过 app.config 设置它没有区别)。

Are there any other settings in .NET or on my machine that can influence the performance? (I am running Vista, but I see the same problem on Windows Server 2003).

.NET 或我的机器上是否还有其他设置会影响性能?(我运行的是 Vista,但我在 Windows Server 2003 上看到了同样的问题)。

Perhaps some restrictions on how many connections a single process can make?

也许对单个进程可以建立多少个连接有一些限制?

回答by shaunf

have you tried increasing the max connections in the network settings?

您是否尝试过在网络设置中增加最大连接数?

http://msdn.microsoft.com/en-us/library/fb6y0fyc.aspx

http://msdn.microsoft.com/en-us/library/fb6y0fyc.aspx

回答by LoveMeSomeCode

How are you creating your threads? I assume that since you know you have 80 threads, you're not using the threadpool manager, because with the threadpool manager you can ask for as many threads as you like and you'll only get 25 active threads at a time. If you create the threads manually with an array then you'll actually get as many as you need, however they are still in the same process space, so that might limit them over threads running in separate processes.

你如何创建你的线程?我假设因为您知道您有 80 个线程,所以您没有使用线程池管理器,因为使用线程池管理器您可以请求任意数量的线程,并且一次只能获得 25 个活动线程。如果您使用数组手动创建线程,那么您实际上将获得所需数量的线程,但是它们仍位于相同的进程空间中,因此可能会限制它们在不同进程中运行的线程。

You might also look into which apartment style the threads are getting created with, I believe the Thread class ctor uses STA by default. Try MTA and see if they affects performance.

您还可以查看创建线程的单元样式,我相信 Thread 类构造函数默认使用 STA。尝试 MTA,看看它们是否会影响性能。

回答by Peter Oehlert

Keep in mind multithreaded code can always cause contention on any shared resources and even if you're not explicitly sharing anything you might be using classes that are sharing resources under the covers.

请记住,多线程代码总是会导致对任何共享资源的争用,即使您没有明确共享任何内容,您也可能使用在幕后共享资源的类。

If you are really getting better performance with 2 40 thread exes than 1 80 thread exe then you'll need to start your investigation with shared resources. And if that is the case, the code you cited is far less interesting than the code that creates and manages the threads.

如果使用 2 40 线程 exe 确实比使用 1 80 线程 exe 获得更好的性能,那么您需要开始使用共享资源进行调查。如果是这种情况,那么您引用的代码远没有创建和管理线程的代码有趣。

The other thing I'd throw out there is there are several tools that you can get that will do this type of thing for you generically. See http://support.microsoft.com/kb/231282. Also included in Visual Studio (I'm not sure what skus) is a new generation of web application performance testing tools. And I'm sure if you looked you could find some non-MS stuff too.

我要抛出的另一件事是,您可以获得几种工具,它们通常可以为您执行此类操作。请参阅http://support.microsoft.com/kb/231282。还包括在 Visual Studio(我不确定什么是 skus)中的是新一代的 Web 应用程序性能测试工具。而且我敢肯定,如果您仔细查看,您也可以找到一些非 MS 的东西。

回答by Jader Dias

You must set the maxconnection parameter at the app.config or web.config file:

您必须在 app.config 或 web.config 文件中设置 maxconnection 参数:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.net>
    <connectionManagement>
      <add address="*" maxconnection="80"/>
    </connectionManagement>
  </system.net>
</configuration>

Values up to 100 work very well with Windows XP.

高达 100 的值适用于 Windows XP。

Update:I just found out that the method above is an alternative way to set the System.Net.ServicePointManager.DefaultConnectionLimit

更新:我刚刚发现上面的方法是设置 System.Net.ServicePointManager.DefaultConnectionLimit 的另一种方法

回答by Dreamer

There are two important aspects w.r.t performance:

wrt性能有两个重要方面:

  1. One aspect is as suggested by all, the number of TCP connectionsbeen used by client (generally better if these connections are persisted (keep alive = true)) for detail refer to : http://msdn.microsoft.com/en-us/library/system.net.servicepoint.connectionlimit(v=vs.110).aspx, How and where the TCP connection has been created in httpwebrequest, and how is it related to servicepoint?, Why System.Net.ServicePoint.ConnectionLimit uses '7FFFFFFF' (Int32.MaxValue/2147483647) when a client connects to a service on 'localhost'?, System.Net.ServicePointManager.DefaultConnectionLimit and .MaxServicePointIdleTime)

  2. Second aspect is rather than using multiple new threads/or using worker threads to do work in parallel using synchronous calls (like httpwebrequest.getrequeststream) in the code snippet, embracing async model completely(for ex, begin/endrequeststream, or new task variations). This way CPU will be always busy and let I/O completion port thread simply send the response in a worker (thread pool) thread by invoking callback. (you may refer to: How does .NET make use of IO Threads or IO Completion Ports?, http://blog.marcgravell.com/2009/02/async-without-pain.html, HttpWebRequest and I/O completion ports)

  1. 一方面正如大家所建议的那样,客户端使用的 TCP 连接数(如果这些连接被持久化,通常会更好(keep alive = true))详细参考:http: //msdn.microsoft.com/en-us /library/system.net.servicepoint.connectionlimit(v=vs.110).aspx, httpwebrequest中 TCP 连接是如何创建的,在哪里创建的,它和servicepoint有什么关系?,为什么 System.Net.ServicePoint.ConnectionLimit 在客户端连接到 'localhost' 上的服务时使用 '7FFFFFFF' (Int32.MaxValue/2147483647)?, System.Net.ServicePointManager.DefaultConnectionLimit 和 .MaxServicePointIdleTime)

  2. 第二个方面不是使用多个新线程/或使用工作线程在代码片段中使用同步调用(如 httpwebrequest.getrequeststream)并行工作,完全拥抱异步模型(例如,begin/endrequeststream 或新任务变体) . 这样 CPU 将始终处于忙碌状态,并让 I/O 完成端口线程通过调用回调在工作线程(线程池)线程中简单地发送响应。(您可以参考:.NET 如何使用 IO 线程或 IO 完成端口?http: //blog.marcgravell.com/2009/02/async-without-pain.htmlHttpWebRequest 和 I/O 完成端口)