展望未来,我们希望对这个过程有更多的控制,并实现某种虚拟的队列。当前的负载平衡器没有排队功能,只是根据速率限制简单地允许流量通过。这是随机的,当您刷新页面(或获得自动刷新)时,运气好转。
我已经在线阅读了这一点,但发现了一些关于如何实现一个非常基本的虚拟HTTP请求队列的实现细节。当然,公司当然可以提供这样一个完整的服务,比如queue-it和Netprecept,但是对于我们目前的需求来说,这些服务似乎太过瘾了(而且非常昂贵)。
有问题的Web应用程序是用ASP.Net MVC编写的。考虑到我们目前不需要“队列优先级”等高级功能,我已经使用静态队列管理器类创建了一个非常基本的概念验证,使用ConcurrentQueue< T>但是我想知道这是否是一种有效的可扩展方法?这是可以成为主要应用层的一部分吗?还是应该分开?有没有人有任何技术知识如何实现这种功能到ASP.Net MVC应用程序?
编辑:感谢至今的答案。大多数答案似乎都涉及到关于缓存的很多细节。这已经(非常)在我们的网站上使用,使用ASP.Net Web缓存,缓存在负载均衡器级别的全页请求和使用AppFabric的对象缓存。
管理队列的能力的原因是因为进程非常数据库写入重。我们通过网站有效地为特定产品创建订单。这意味着这些DB交易正在考虑到最后一刻的库存检查等等。这是性能问题出现的地方,这就是想要实现某种排队系统的原因。
在数据库服务器上投入更多的资源不是一个现实的选择。我真的在寻找这种性质的排队系统(C#或其他)的技术实现的细节。对不起,如果这没有明确说明
解决方法
>缓存
>无会话控制器
> AsyncControllers
输出缓存:
也许MVC3(Performance-Wise)最有用的功能是输出缓存。当您的应用程序真的必须获取数据,对其进行计算并返回数据时,最大的性能匹配实际发生。输出缓存可以缓存这些结果,以便它们可以直接返回,甚至不用触摸数据库。特别是当执行复杂的查询时,可以显着降低服务器上的负载(实际上,您可以通过仔细地在Web应用程序中实现缓存,将90%的百分比丢弃在服务器上)。
namespace MvcApplication1.Controllers { public class DataController : Controller { [OutputCache(Duration=10)] public string Index() { return DateTime.Now.ToString("T"); } } }
无会话控制器
禁用会话状态的控制器为不需要会话状态的控制器提供优化。无状态控制器适用于您不需要会话概念的情况。
默认情况下,ASP.NET管道将不会同时处理属于同一会话的请求。它将它们连接起来,即按照它们被接收的顺序排列它们,使得它们被串行地而不是并行地处理。这意味着如果请求正在进行,并且来自同一会话的另一个请求到达,则它将被排队,以便在第一个请求完成时才开始执行。
我们来看一个例子一个向服务器发出3个异步AJAX请求的页面,启用了会话状态(也注意到实际上必须使用会话),因为如果您不使用会话状态(即使启用了),ASP.NET也不会串行化请求。
JQuery的
$(document).ready(function () { //Make 3 concurrent requests to /ajaxtest/test for (var i = 0; i < 3; i++) { $.post("/ajaxtest/test/" + i,function (data) { //Do something with data... },"json"); } });
控制器 – 动作方法
public class AjaxTestController : Controller { [HttpPost] public JsonResult Test(int? id) { Thread.Sleep(500); return Json(/*Some object*/); } }
您可以在网络配置文件中看到序列化请求的影响;每个请求比前一个请求大约长500ms。所以这意味着我们不会以异步方式使这些AJAX调用获得任何好处。让我们再次看看我们的AjaxTestController禁用了会话状态的配置文件(使用[SessionState]属性))。
[SessionState(SessionStateBehavior.Disabled)] public class AjaxTestController : Controller { //...As above }
好多了!您可以看到3个请求如何并行处理,总共需要500ms才能完成,而不是我们在第一个示例中看到的1500ms。
异步控制器:
首先,控制器开始一个或多个外部I / O调用(例如,sql数据库调用或Web服务调用)。没有等待他们完成,它将线程释放回ASP.NET工作线程池,以便它可以处理其他请求。
之后,当所有外部I / O调用都完成时,底层的ASP.NET平台从池中获取另一个免费的工作线程,将其重新附加到原始HTTP上下文中,并使其完整地处理原始请求。
How to Measure the Response time under Heavy Traffic?
要了解异步控制器如何响应不同级别的流量,以及如何与简单的同步控制器进行比较,您可以使用两个控制器创建一个示例MVC。要模拟长时间运行的外部程序,它们都执行需要2秒钟的SQL查询(使用sql命令WAITFOR DELAY ’00:00:02′),然后它们将相同的固定文本返回到浏览器。其中一个同步进行;另一个异步。
在另一个例子中,您可以检查一个简单的C#控制台应用程序,模拟大量流量击中给定的URL。它只是反复要求相同的URL,计算最后几个响应时间的滚动平均值。首先,它只在一个线程上这样做,但是在30分钟内逐渐将并发线程数增加到150个。如果要尝试针对自己的站点运行此工具,可以下载C#源代码。
结果说明了异步请求如何执行的一些观点。查看平均响应时间图与并发请求数(响应时间较短):
要理解这一点,首先我需要告诉你,我将ASP.NET MVC应用程序的工作线程池设置为人为的最低限制为50个工作线程。实际上,我的服务器的默认最大线程池大小为200,这是一个更明智的限制 – 但是如果我减少它,结果会更清晰。
正如你所看到的,只要有足够的工作线程绕过,同步和异步请求就会完全相同。为什么不呢?
但是,一旦线程池耗尽(> 50客户端),同步请求必须形成一个队列来进行服务。基本排队理论告诉我们,队列中等待的平均时间由公式给出:
这正是我们在图表中看到的。排队时间与队列的长度呈线性增长。 (抱歉,我放纵使用公式 – 有时我只是不能压制我的内在数学家,如果成为一个问题,我会得到治疗。)
不过,异步请求不需要尽快启动排队。他们在等待时不需要阻止工作线程,因此线程池限制不是问题。那么为什么当有超过100个客户端时,他们开始排队?这是因为默认情况下,ADO.NET连接池限制为100个并发连接。
希望这可以帮助你。