【问题标题】:Long running operations (threads) in a web (asp.net) environmentWeb (asp.net) 环境中长时间运行的操作(线程)
【发布时间】:2010-05-11 07:07:52
【问题描述】:

我有一个 asp.net (mvc) 网站。作为功​​能的一部分,我将不得不支持一些长时间运行的操作,例如:

从用户发起: 用户可以上传(xml)文件到服务器。在服务器上,我需要提取文件,进行一些操作(插入数据库)等......这可能需要一分钟到十分钟(甚至更长时间 - 取决于文件大小)。当然,我不想在导入运行时阻止请求,但我想将用户重定向到某个进度页面,他将有机会查看状态、错误甚至取消导入。

此操作不会经常使用,但可能会发生两个用户同时尝试导入数据的情况。并行运行导入会很好。一开始我想在 iis(控制器操作)中创建一个新线程并在新线程中运行导入。但我不确定这是否是个好主意(在 Web 服务器上创建工作线程)。我应该使用 Windows 服务还是任何其他方法?

从系统启动: - 我将不得不定期用新数据更新 lucene 索引。 - 我将不得不发送大量电子邮件(将来)。

我应该将其作为站点中的作业实现并通过 Quartz.net 运行作业,还是还应该创建 Windows 服务或其他什么?

在运行站点“作业”方面有哪些最佳做法?

谢谢!

【问题讨论】:

标签: asp.net asp.net-mvc multithreading long-running-processes


【解决方案1】:

我会为长时间运行的任务实现独立的 Windows 服务。 Web 应用程序通过队列方法将长时间运行的任务委托给该服务。如何组织任务队列取决于您。排队的任务是否具有优先级、最大执行时间。队列可以实现为 DBMS 中的普通表,其中包含作业执行状态信息属性(非常简单的方法)。

所以常见的场景可能如下所示:

  • 客户端将所有需要的信息发送到网络服务器

  • Web 服务器将任务委托给服务 并通知客户 - 任务是 成功排队(任务 ID 也发送给客户端)

  • 外部服务启动任务处理, 更新进度信息。

  • 客户端开始轮询 Web 服务器 关于作业的简短执行请求(之前收到的 id) 状态和进度。

您可以选择不同的技术(Windows 服务 + DB / WCF 服务)和不同的通信方式(轮询、推送、回调),但我建议将长时间运行的任务委托给外部服务(不要在 Web 应用程序中执行它们)。

执行长时间运行的任务会带来每个请求线程的模型(在多线程编程术语中)。这种模型具有较差的可扩展性和线程池最大线程数的限制。不过这不是你的情况:)

【讨论】:

  • 我曾经以这种方式进行大量转换,报告生成,数据生成。这是唯一合乎逻辑的选择。
【解决方案2】:

在我看来,长时间运行的任务通常应始终委派给非基于 UI 的操作。我建议也许是 WF 或 Window 服务。

【讨论】:

    【解决方案3】:

    我已经使用 jQuery 成功实现了与此类似的场景。基本上,我使用beforeSend: 函数来显示“请稍候”类型的页面。这是正在运行的基本代码(我也没有,您也可以使用AsyncController 基类来使操作异步):

    <script type="text/javascript">
        $(document).ready(function() {
            $('#create').bind('click', function() {
                saveFundProperty();
            });
        });
    
        // main functions
        function saveFundProperty() {
            var url = '<%= Url.Action("Create", "FundProperty") %>';
            var params = { fundId: $("#FundID").val(), propertyId: $("#PropertyID").val() };
            SendAjax(url, params, beforeQuery, saveFundPropertyResponse);
        }
    
        function beforeQuery() {
            var url = '<%= Url.Action("Wait", "FundProperty") %>';
            $("#statusMsg").load(url);
        }
    
        function saveFundPropertyResponse(data) {
            if (data.length != 0) {
                if (data.indexOf("ERROR:") >= 0) {
                    $("#statusMsg").html(data).css('backgroundColor','#eeaa00');
                }
                else {
                    $("#statusMsg").html(data);
                }
            }
        }
    </script>
    

    希望这会有所帮助。

    SendAjax 方法纯粹是一个包装函数,以使事情更加一致。这是完整的:

    <script type="text/javascript">
    function SendAjax(urlMethod, jsonData, beforeSendFunction, returnFunction, dataType, contentType) {
        $.ajaxSetup({ cache: false });
        dataType = dataType || "text"; // default return type
        contentType = contentType || "application/x-www-form-urlencoded"; // default input type
        $.ajax({
            type: "POST",
            url: urlMethod,
            data: jsonData,
            dataType: dataType,
            contentType: contentType,
            beforeSend: function() {
                if(beforeSendFunction!==null)
                    beforeSendFunction();
            },
            success: function(data) {
                // Do something interesting here.
                if (data != null && returnFunction!==null) {
                    returnFunction(data);
                }
            },
            error: function(xhr, status, error) {
                // Boil the ASP.NET AJAX error down to JSON.
                var err = eval("(" + xhr.responseText + ")");
    
                // Display the specific error raised by the server
                alert(err.Message);
            }
        });
    }
    </script>
    

    [编辑] - 不确定 SendAjax 格式发生了什么。希望很容易复制/粘贴...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-03
      • 1970-01-01
      • 2011-01-10
      • 2019-08-03
      • 1970-01-01
      • 1970-01-01
      • 2011-04-15
      • 1970-01-01
      相关资源
      最近更新 更多