HttpWebResponse获胜';t并发出站请求的规模
本文关键字:请求 并发 获胜 HttpWebResponse | 更新日期: 2023-09-27 18:29:57
我有一个用C#编写的ASP.NET 3.5服务器应用程序。它使用HttpWebRequest和HttpWebResponse向REST API发出出站请求。
我已经设置了一个测试应用程序,以便在单独的线程上发送这些请求(以模糊地模拟针对服务器的并发)。
请注意,这更像是一个Mono/Environment问题,而不是一个代码问题;所以请记住,下面的代码不是逐字逐句的;只是功能位的剪切/粘贴。
这里有一些伪代码:
// 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)上运行该应用程序,无论是1"用户"还是100,我都可以上调numThreads并接收相同的平均响应时间。
在Mono 2.10.2环境中将应用程序发布并部署到Apache2上,响应时间几乎是线性的。(即,1个线程=300ms,5个线程=1500ms,10个线程=3000ms)。无论服务器端点(不同的主机名、不同的网络等)如何,都会发生这种情况。
使用IPTRAF(和其他网络工具),应用程序似乎只打开1或2个端口来路由所有连接,其余的响应必须等待。
我们已经构建了一个类似的PHP应用程序,并在Mono中部署了相同的请求和相应的响应。
我已经运行了我能想到的Mono和Apache的每一个配置设置,这两个环境之间唯一不同的设置(至少在代码中)是,有时Mono中的ServicePoint SupportsPipelining=false,而在我的机器上是true。
由于某种原因,Mono中的ConnectionLimit(默认值2)似乎没有更改,但我在代码和指定主机的web.config中都将其设置为更高的值。
要么我和我的团队忽略了一些重要的东西,要么这是Mono中的某种bug。
我相信您在HttpWebRequest
中遇到了瓶颈。每个web请求都使用.NET框架中的公共服务点基础结构。这似乎是为了允许重用对同一主机的请求,但根据我的经验,这会导致两个瓶颈。
首先,为了符合HTTP规范,服务点默认情况下只允许到给定主机的两个并发连接。这可以通过将静态属性ServicePointManager.DefaultConnectionLimit
设置为更高的值来覆盖。有关详细信息,请参阅此MSDN页面。看起来您已经为单个服务点本身解决了这个问题,但由于服务点级别的并发锁定方案,这样做可能会导致瓶颈。
其次,ServicePoint
类本身的锁粒度似乎存在问题。如果您反编译并查看lock
关键字的源代码,您会发现它使用实例本身进行同步,并且在许多地方都是这样做的。在给定主机的web请求之间共享服务点实例的情况下,根据我的经验,随着打开更多的HttpWebRequests
,这往往会成为瓶颈,并导致其扩展性较差。第二点主要是个人观察和探源,所以对此持怀疑态度;我不认为这是一个权威的消息来源。
不幸的是,在我使用它的时候,我没有找到一个合理的替代品。现在ASP.NETWebneneneba API已经发布,你可能希望看看HttpClient。希望能有所帮助。
我知道这很旧,但我把它放在这里,以防它可能会帮助其他遇到这个问题的人。并行出站HTTPS请求也遇到了同样的问题。有几个问题在起作用。
第一个问题是,据我所知,ServicePointManager.DefaultConnectionLimit
没有更改连接限制。将此值设置为50,创建一个新连接,然后检查服务点上新连接的连接限制为2。一旦在该服务点上将其设置为50,对于最终将通过该服务点的所有连接似乎都有效并持久。
我们遇到的第二个问题是线程。单线程池的当前实现似乎每秒最多创建2个新线程。如果你正在做许多在同一时间开始的并行请求,这将是永恒的。为了抵消这种情况,我们尝试将ThreadPool.SetMinThreads设置为更高的数字。当您进行此调用时,Mono似乎最多只创建1个新线程,而不管当前线程数和所需线程数之间的增量如何。我们可以通过在循环中调用SetMinThreads来解决这个问题,直到线程池具有所需数量的空闲线程。
我打开了一个关于后一个问题的bug,因为这是我最确信没有按预期工作的问题:https://bugzilla.xamarin.com/show_bug.cgi?id=7055
如果@jake-moshenko关于ServicePointManager.DefaultConnectionLimit
在Mono中更改后没有任何效果的说法是正确的,请将其作为错误提交http://bugzilla.xamarin.com/.
然而,在将其作为Mono问题完全丢弃之前,我会尝试一些事情:
- 通过将
--gc=sgen
作为标志传递给mono,尝试使用SGen垃圾收集器而不是旧的boehm垃圾收集器 - 如果以上内容没有帮助,请升级到Mono 3.2(BTW也默认为SGEN GC),因为自从您提出这个问题以来,已经进行了很多修复
- 如果以上内容没有帮助,请构建自己的Mono(主分支),因为这个关于线程的重要pull请求最近已经合并
- 如果以上内容没有帮助,请添加此pull请求来构建您自己的Mono。如果它解决了您的问题,请在拉取请求中添加"+1"。这可能是对错误7055的修复