在Web应用程序中排队长时间运行的任务

时间:2014-08-18 22:59:08

标签: asp.net .net asynchronous queue

用户可以在我们的网络应用程序上执行操作,该操作需要100毫秒到10秒,我希望立即将结果返回给浏览器,然后在任务完成处理后向用户显示结果。该操作正在同步来自第三方的数据,并实现为类库(DLL)。

通常建议使用像RabbitMQ或MSMQ这样的队列,并让一个工作人员将结果写入数据库,该数据库由来自浏览器的AJAX请求进行轮询,以检查更新。

然而,目标是减少延迟,使其尽可能接近同步运行任务,同时能够处理长时间运行任务的峰值而不影响其余任务网站。

如何构建后端?在我看来,过程将是:启动任务,以最小延迟运行任务,通知最终用户任务完成(ASAP),最后在浏览器中显示结果。

Long Running Task. Credits: Haishi. Source: <code>http://haishibai.blogspot.co.uk/2012/12/dealing-with-long-running-jobs.html</code>

实施例

使用http://www.xml-sitemaps.com/生成站点地图使用分块传输编码每秒发送一个<script>标记,以调用Javascript函数来更新具有最新状态的页面。

使用https://www.ssllabs.com/ssltest/检查SSL证书似乎刷新了整个页面的更新状态。

4 个答案:

答案 0 :(得分:4)

这种情况相对简单,我根本不建议投票。

考虑使用常规Ajax方法:页面的一部分能够在没有页面其余部分的情况下刷新。因此该部分(ajax部分)本身是同步的,但是从整个页面的角度来看是异步的(因为它刷新而不重新加载整个页面)。

因此,当需要计算该信息时,页面的ajax部分将作为常规请求提交。完成请求处理后,页面的该部分可以立即访问响应并显示结果。

优点是您没有轮询开销,并且结果会立即显示在屏幕上(尽快 - 如您所知)。此外,只有一个请求正在进行此操作,而不是在轮询时可能会丢失几个请求。

答案 1 :(得分:3)

您是否考虑过将WF4与SignalR结合使用?

我们使用WF4来处理后端处理,它的表现相当不错。我们将请求存储在作业请求表中,工作流引擎(我们编写的在后端运行wf4的服务)获取请求,处理工作,然后将作业标记为已完成。

然后可以使用SignalR通知客户端作业已完成。扩展相对容易(因为我知道轻松而且总是充满细节),因为您可以提供更多服务来处理请求。每个引擎都会将请求标记为正在处理,以便其他人不会将其提取。

我在大型项目中使用了wf4,这些项目的服务负载均衡,我们能够获得非常不错的吞吐量。

答案 2 :(得分:2)

FWIW,如果您真的不想投资基于全规模队列的解决方案,您可以利用TPL + SignalR(或任何此类Comet库)来处理您的长期运行请求并向客户发送反馈。

所以这个想法是:

  • 客户端向服务器发送请求
  • 服务器通过TPL启动后台处理
  • 服务器通过SignalR
  • 通知客户端更新

像这样(使用TPL和SignalR):

//服务器

public class MyHub : Hub
{

    public void Start()
    {
         var longRunningTask = Task.Factory.StartNew(() =>
            {
                var someService = new someService();
                // do stuff
                someService.doSomething();
                Clients.All.longRunningTask(<data>);    

            }, TaskCreationOptions.LongRunning);
    }
}

// initiating from your asp.net page (codebehind or via ajax or any which way)
new MyHub().Start();

//客户

var hub = $.connection.myHub;
hub.client.longRunningTask = function (data) {
     // do something with data
}

这是非常低的延迟,不涉及排队,并允许您在更新时推送更新(只需调用Clients.All.longRunningTask将更新推送到客户端)。

参考文献:

Getting Started with SignalR 2

答案 3 :(得分:1)

这可能有点过于简单,但最简单的方法可能是对客户端进行长时间轮询。

ie:在GET查询中发送工作请求,获取令牌。然后立即请求该令牌的结果,让服务器阻止响应。如果请求超时,请重新启动它。在服务器端,只需在等待任务完成时阻塞该线程。即使是最具侵略性的负载平衡器也可以提供长达一分钟的挂起时间。

通过在客户端上控制队列,您可以通过禁用提交或使用jQuery.ajaxPrefilter(或任何其他感觉正确的方法)人为地限制未完成的服务器请求数量,更轻松地防止客户端进行积极点击给你。)

或者您可以投资websocket通信层,以便服务器可以主动ping客户端,但长轮询是套接字的常见后备,这对于这个用例来说已经足够了(我认为< / em> ...取决于你需要这些电话的频率/次数。