Web Api(mvc,asp.net),订阅推送和长期运行的任务

时间:2016-06-02 12:48:40

标签: asp.net asynchronous asp.net-web-api signalr

我正在开发一个项目,旨在为客户提供有关相对复杂数据的一些观点。 (至少)有两种方法可以完成这项任务

  1. 从db读取数据,“转储”到客户端,让JS客户端执行所有计算

  2. 服务器端(C#)从数据库加载数据,适合“轻量级”JS客户端的烹饪数据(基本上,D3图表或DataTables形式的烹饪数据可以立即理解)。

  3. 方法(1)是完全有效的,只是不相信它现在真正适合现有的基础设施 目前正在实施方法(2),其工作原理如下 html的:

    <table id="table1" ...></table>
    <table id="table2" ...></table>
    <table id="table3" ...></table>
    <div id="chartX"/>
    ....
    $(document).ready(function () {
    //initialize a table directly with url to api call:
        $('#table1').dataTable({
            ajax: {
                url: 'http://app/api/methodZ?param=value',
                dataSrc: '',
            },
         });
    // or do a call and initialize table(s)
        $.ajax({
            url: 'http://app/api/methodA?param1=value1&param2=value2',
            success: function (result) {
                populatetable2(result.table2Data);
                populatetable3(result.table3Data);
            },
        });
    

    我会说非常标准的方法。问题如下

    1. API调用是cpu且耗时
    2. 某些电话可并行化
    3. 填充单个页面的客户端视图需要太多调用
    4. 有些调用是依赖的(例如,api方法methodA是自包含的,时间/ cpu消耗,medthodB也是时间/ cpu消耗,需要作为其逻辑的一部分来运行方法A)
    5. 我的想法如下:

      1. 客户端只调用一种方法,例如“subscribe”
      2. Web API服务器端在后台启动任务并推送(通过SignalR)部分结果
      3. 客户端接收数据并逐步填写UI
      4. html的:

        <table id="table1" ...></table>
        <table id="table2" ...></table>
        <table id="table3" ...></table>
        <div id="chartX"/>
        ....
        <script src="~/signalr/hubs"></script>
        ....
        $(document).ready(function () {
            $.ajax({
                url: 'http://app/api/subscribe?paramA=valueA',
            }); 
        });
        
        WorkerControllerHub.client.OnDataAReady = function (data) {
           //fill UI 
        }
        
        WorkerControllerHub.client.OnDataBReady = function (data) {
           //fill UI 
        }
        ...
        WorkerControllerHub.client.OnComplete = function (iserror, message) {
           //nicely notify user if there is an error or all data is loaded
        }
        

        服务器端会有点

        WorkerControllerHub { /*SignalR hub*/ }
        WorkerController
        {
            private void doAsync(int paramA)
            {
                var dataA = calculateA(paramA);
                WorkerControllerHub.OnDataAReady(dataA);
        
                var dataB = calculateB(paramB, dataB);
                WorkerControllerHub.OnDataAReady(dataA); //
        
                WorkerControllerHub.OnComplete(...);
            }
        
            public void Subscribe(int paramA)
            {
                 sumbitAsyncJob({task/async/... doAsync with paramA});
                 and immediate return from controller call
            }
        }
        

        我想说,根据我以前使用分布式应用程序进行的C ++或Java服务器端项目的经验,它是相对标准的方法。 (因为相当一些计算A,计算B,......是互连的,它实际上是一种首选方式)。但是,阅读SO,似乎这不是采取的方法 - 长时间运行的异步任务不适合IIS + WebAPI + ASP.NET。

        因此,我的问题是 - 是否存在解决此问题的架构建议?

        感谢。

2 个答案:

答案 0 :(得分:1)

当然,我会在服务器端进行计算。

长时间运行的功能(工作)
我猜您的计算可以启动:
  - 来自客户端的来自客户端,调用启动作业的异步void hub方法   - 按计划使用FluentScheduler之类的内容。您可以为作业设置计划

SignalR Broadcasting
您的客户可以加入不同的groups,具体取决于需要更新该页面的组件 您可以实现类似Broadcaster类(在作业完成后调用)并将数据发送给用户。

public class Broadcaster
{
    private static void BroadcastToGroup(Data data, string groupName)
    {
        var clients = GlobalHost.ConnectionManager.GetHubContext<MyHub>().Clients;
        clients.Group(groupName).UpdateData(data);
    }

    private static void BroadcastToAll(Data data)
    {
        var clients = GlobalHost.ConnectionManager.GetHubContext<MyHub>().Clients;
        clients.All.UpdateData(data);
    }
}

<强>并发
您可能遇到的长时间运行作业的一个问题是,作业的一个实例可以在前一个作业完成之前启动。这可能会导致服务器的开销 解决此问题的一种方法是使用作业计划程序并将作业设置为不可重入。再看一下FluentScheduler

<强>回收
如果您说您的计算需要一段时间,则可能会遇到IIS Process Recycling的问题。基本上,每隔29小时(默认情况下),IIS会尝试停止所有有问题的线程,并可能将您的工作视为一个 请阅读此great article,了解如何解决此问题。

答案 1 :(得分:1)

你的方法是合理的,你只需要有一个监听服务器方法的客户端方法,每当你有一大块数据时,你可以将它广播到客户端,客户端可以操作并将数据附加到客户端视图。您甚至不需要使用多种方法,只需操作客户端上接收的数据即可。您可以等待所有数据返回,或者在客户端上根据需要附加数据,或者在服务器上更新数据并在将数据发送到客户端之前对其进行组织,并让客户端覆盖数据正如它收到的那样。