HttpWebResponse不会扩展并发出站请求

时间:2012-04-03 16:04:29

标签: c# apache mono httpwebresponse servicepoint

我有一个用C#编写的ASP.NET 3.5服务器应用程序。它使用HttpWebRequest和HttpWebResponse向REST API发出出站请求。

我已经设置了一个测试应用程序来在不同的线程上发送这些请求(模糊地模仿服务器的并发性)。

请注意,这更像是单声道/环境问题,而不是代码问题;所以请记住,下面的代码不是逐字的;只是功能位的剪切/粘贴。

这是一些伪代码:

// threaded client piece
int numThreads = 1;
ManualResetEvent doneEvent;

using (doneEvent = new ManualResetEvent(false))
        {

            for (int i = 0; i < numThreads; i++)
            {

                ThreadPool.QueueUserWorkItem(new WaitCallback(Test), random_url_to_same_host);

            }
            doneEvent.WaitOne();
        }

void Test(object some_url)
{
    // setup service point here just to show what config settings Im using
    ServicePoint lgsp = ServicePointManager.FindServicePoint(new Uri(some_url.ToString()));

        // set these to optimal for MONO and .NET
        lgsp.Expect100Continue = false;
        lgsp.ConnectionLimit = 100;
        lgsp.UseNagleAlgorithm = true;
        lgsp.MaxIdleTime = 100000;        

    _request = (HttpWebRequest)WebRequest.Create(some_url);


    using (HttpWebResponse _response = (HttpWebResponse)_request.GetResponse())
    {
      // do stuff
    } // releases the response object

    // close out threading stuff

    if (Interlocked.Decrement(ref numThreads) == 0)
    {
        doneEvent.Set();
    }
}

如果我在Visual Studio Web服务器上的本地开发计算机(Windows 7)上运行该应用程序,我可以使用numThreads并获得相同的平均响应时间,只需要1“用户”或100。

在Mono 2.10.2环境中发布和部署应用程序到Apache2,响应时间几乎是线性的。 (即,1个线程= 300毫秒,5个线程= 1500毫秒,10个线程= 3000毫秒)。无论服务器端点(不同的主机名,不同的网络等)如何,都会发生这种情况。

使用IPTRAF(和其他网络工具),似乎应用程序只打开1或2个端口来路由所有连接,其余响应必须等待。

我们构建了一个类似的PHP应用程序,并在Mono中部署了相同的请求,响应也得到了适当的扩展。

我已经完成了我能想到的Mono和Apache的每个配置设置,并且两个环境之间的唯一设置(至少在代码中)是有时在Mono中ServicePoint SupportsPipelining = false,而它是我的机器确实如此。

似乎ConnectionLimit(默认值为2)由于某种原因未在Mono中更改,但我在代码和指定主机的web.config中将其设置为更高的值。

我和我的团队都忽略了一些重要的事情,或者这是Mono中的某种错误。

3 个答案:

答案 0 :(得分:8)

我相信你在HttpWebRequest遇到了瓶颈。 Web请求每个都使用.NET框架内的公共服务点基础结构。这似乎是为了允许重用对同一主机的请求,但根据我的经验导致两个瓶颈。

首先,默认情况下,服务点仅允许与给定主机的两个并发连接,以便符合HTTP规范。可以通过将静态属性ServicePointManager.DefaultConnectionLimit设置为更高的值来覆盖此值。有关详细信息,请参阅此MSDN页面。看起来您已经为单个服务点本身解决了这个问题,但是由于服务点级别的并发锁定方案,这样做可能会导致瓶颈。

其次,ServicePoint类本身的锁粒度似乎存在问题。如果您反编译并查看lock关键字的来源,您会发现它使用实例本身进行同步,并在许多地方执行此操作。由于服务点实例在给定主机的Web请求之间共享,根据我的经验,随着更多HttpWebRequests被打开并导致其扩展性差,这往往会出现瓶颈。第二点主要是个人观察和在源头周围戳,所以拿一粒盐;我不认为它是权威来源。

不幸的是,在我使用它的时候,我没有找到合理的替代品。既然ASP.NET Web API已经发布,您可能希望看一下HttpClient。希望有所帮助。

答案 1 :(得分:8)

我知道这已经很老了但是我把它放在这里,以防它可能会帮助遇到这个问题的其他人。我们遇到了与并行出站HTTPS请求相同的问题。有一些问题在起作用。

第一个问题是ServicePointManager.DefaultConnectionLimit并没有改变连接限制,据我所知。将此设置为50,创建新连接,然后检查新连接的服务点上的连接限制,请执行以下操作:2。将该服务点上的设置设置为50一次似乎可以正常工作并保留所有将最终通过的连接服务点。

我们遇到的第二个问题是线程问题。单线程池的当前实现似乎每秒最多创建2个新线程。如果您在同一时间开始执行许多并行请求,那么这将是永恒的。为了抵消这种情况,我们尝试将ThreadPool.SetMinThreads设置为更高的数字。无论当前线程数与所需数量之间的差值如何,Mono只会在您进行此调用时创建最多1个新线程。我们能够通过在循环中调用SetMinThreads来解决这个问题,直到线程池具有所需数量的空闲线程。

我打开了关于后一个问题的错误,因为那是我最有信心的那个没有按预期工作的:https://bugzilla.xamarin.com/show_bug.cgi?id=7055

答案 2 :(得分:0)

如果@ jake-moshenko对于ServicePointManager.DefaultConnectionLimit如果在Mono中更改没有任何效果是正确的,请将其作为http://bugzilla.xamarin.com/中的错误提交。

但是,我会尝试一些事情,然后将其作为Mono问题完全丢弃:

  1. --gc=sgen作为标志传递给单声道,尝试使用SGen垃圾收集器而不是旧的boehm垃圾收集器。
  2. 如果以上情况没有帮助,请升级到Mono 3.2(BTW默认为SGEN GC),因为自从您提出问题以来已经有很多修复。
  3. 如果上述方法没有帮助,请构建您自己的Mono(主分支),因为this important pull request最近已经合并了线程。
  4. 如果上述方法无效,请添加this pull request来构建自己的Mono。如果它解决了您的问题,请在拉取请求中添加“+1”。它可能是bug 7055的修复。