我有一个用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中的某种错误。
答案 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问题完全丢弃:
--gc=sgen
作为标志传递给单声道,尝试使用SGen垃圾收集器而不是旧的boehm垃圾收集器。