【问题标题】:keep load balancer from timing out during long operation防止负载均衡器在长时间运行期间超时
【发布时间】:2012-10-04 22:21:56
【问题描述】:

我正在使用 ASP.NET MVC 4 来接受最大为 30 mb 的视频上传。将视频上传到站点后,然后使用 HttpWebRequest 将视频发布到第 3 方 Web 服务。在我的网站可以向浏览器返回响应之前,必须完成向 3rd 方服务的上传并返回响应。在 Rackspace 云站点上,负载均衡器有 30 秒的超时。如果负载均衡器在 30 秒内没有从我的站点返回任何响应,则负载均衡器会向浏览器返回超时错误并终止请求。

所以我被告知需要发生的是,我的网站需要通过将数据发送回浏览器来保持连接,同时我的网站正在将视频发送到第 3 方系统,这样负载平衡器就不会' t 超时。你将如何完成这项任务?

我正在寻找 2 件事 - 在上传过程中,我应该将哪些数据作为数据发送回浏览器?我的实际响应是 JSON,所以如果我能在最后保留我的常规 JSON 响应,那将是理想的。

最后,如何在发送保活数据的同时执行 HttpWebRequest 上传到第三方?

-

作为参考,这是来自 rackspace 云的负载均衡器连接超时文档:http://www.rackspace.com/knowledge_center/article/connection-timed-out-error-message-1

他们没有为这个问题提供任何具体的解决方案。

【问题讨论】:

  • 如果您需要一个快速而肮脏的解决方案来防止机架空间负载均衡器超时,您可以使用我在下面的答案。这是一个完整的 hack,并且不够强大,无法处理大量负载。理想的解决方案是启动长时间执行的操作,将该请求返回给浏览器,然后从浏览器进行某种轮询以检查操作是否已完成。这不是我的解决方案的工作方式。如果您碰巧编写了自己的使用轮询的解决方案,请将其发布在此线程上。谢谢!
  • Rackspace 刚刚宣布他们正在添加一项功能,以便您可以在负载均衡器上设置超时。见这里:feedback.rackspace.com/forums/71021-product-feedback/…

标签: asp.net asp.net-mvc load-balancing


【解决方案1】:

这是我解决问题的方法。此解决方案应适用于任何使用具有不合理超时的负载平衡器的 .net 主机。 Rackspace 云站点的负载均衡器有 30 秒的超时。 Amazon 和 Azure 的超时时间更长,但如果您碰巧有一个长时间运行的操作,这仍然可能是一个有用的解决方案。

在 asp.net mvc 4 中,您可以将控制器更改为从 AsyncController 继承。这允许您分离异步任务,然后等待它们完成。此解决方案创建一个“保持活动状态”任务,该任务每 10 秒通过 asp.net 响应发回数据,以便负载均衡器检测到活动并且不会超时。该任务一直运行,直到它看到uploadFinished 变量设置为true。

另一个任务执行长时间运行的上传,或者在 asp.net mvc 控制器结束它正在处理的 Web 请求之前需要完成的任何任务。长时间运行的操作完成后,它会将 uploadFinished 变量设置为 true。

最后,我围绕实际的 json 响应手动构建了一个容器 json 对象,这样我就可以让“保持活动”数据成为发送回浏览器的有效 json 的一部分。一个有点丑陋的黑客,但它有效!

using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using System.Web.Script.Serialization;

namespace MvcApplication4.Controllers
{
    public class ExampleController : AsyncController
    {

        [HttpPost]
        public string Upload(UploadModel model, HttpPostedFileBase videoFile)
        {
            // when using jquery ajax form for upload
            // IE requires text/plain content type
            // otherwise it will try to open the response as a file
            // instead of pass the response back to your javascript
            if (Request.AcceptTypes.Contains("application/json"))
            {
                Response.ContentType = "application/json";
            }
            else
            {
                Response.ContentType = "text/plain";
            }

            // start json object
            Response.Write("{\"keepalive\":\"");
            Response.Flush();

            Task[] tasks = new Task[2];
            tasks[0] = Task.Factory.StartNew(() => DoKeepAlive());
            tasks[1] = Task.Factory.StartNew(() => DoUpload(model, videoFile));
            Task.WaitAll(tasks);

            // end keepalive json property
            Response.Write("\",\"data\":");

            // insert actual response data
            JavaScriptSerializer json = new JavaScriptSerializer();
            Response.Write(json.Serialize(uploadResponse));

            // end json object
            Response.Write("}");

            return "";
        }

        public bool uploadFinished = false;
        public UploadResponseModel uploadResponse = null;

        public void DoUpload(UploadModel model, HttpPostedFileBase videoFile)
        {
            // do upload to 3rd party
            MyServiceClient c = new MyServiceClient();
            uploadResponse = c.UploadVideo(model, videoFile);
            uploadFinished = true;
        }

        public void DoKeepAlive()
        {
            // send . every 10 seconds
            while (!uploadFinished)
            {
                Response.Write(".");
                Response.Flush();
                Thread.Sleep(10000);
            }
        }
    }
}

此解决方案依赖于 .Net 4 来执行异步操作。如果您有 .Net 4.5,则有更新的方法可以在不依赖 AsyncController 类的 mvc 控制器中执行异步操作。

这个网站很有帮助,并解释了如何在各种版本的 .Net 框架中在 asp.net mvc 中进行异步操作:

http://dotnet.dzone.com/news/net-zone-evolution

【讨论】:

  • 我不建议您实施此解决方案。在上传操作期间,您正在危及服务器上宝贵的工作线程,这可能会很快使其瘫痪,尤其是在您有很多客户端的情况下。更好的解决方案是开始上传文件并立即返回。然后客户端可以不时轮询任务的完成情况。如果您使用 SignalR,它甚至可能是服务器向客户端推送任务已完成的通知。
  • 谢天谢地,这是我网站上不经常调用的操作。我认为,如果您在 .Net 4.5 中使用较新的异步选项,则可以在异步操作在非线程池线程上执行时将工作线程放回线程池。不幸的是,Rackspace Cloud Sites 目前不支持 .Net 4.5。
  • 不,不会放回任何线程。如果您放回工作线程,则响应对象会与它们一起死亡(这是 IO 完成端口的全部意义),并且当响应对象死亡时,您当然会在尝试写入时获得 NRE。除了让客户端轮询完成之外,没有解决此问题的方法。当然,您总是可以使用您的解决方案,我只是在这里为那些对这个解决方案的风险有同样问题的人提到它。在你的情况下,这个动作不经常被调用是很好的,但我个人永远不会那样做。
  • 查看 Stephen Cleary 在这篇文章中的回复:“关于异步的真正酷的事情是 [...] 在下载过程中,await 实际上从异步方法中返回,留下请求线程。该请求线程返回到线程池 - 留下 0(零)个线程服务于该请求。 stackoverflow.com/a/9229660/27497
  • 当然,仅当您使用 IO 完成端口时才如此。在他提供的示例中,他在 WebClient 上使用了 DownloadDataTaskAsync。在你的情况下,你正在旋转一个你正在睡觉的新任务,更糟糕的是你正在等待它在主线程中完成。因此,虽然他的解决方案非常适用于从远程位置下载文件,但如果您开始定期 ping 客户端,您将失去它的全部意义。
【解决方案2】:

很遗憾,您无法为您的站点调整负载平衡器配置。

我想你想做一个 javascript keep-alive。

例如:

(function poll(){
$.ajax({ url: "server", success: function(data){
    //Update your dashboard gauge
    salesGauge.setValue(data.value);

}, dataType: "json", complete: poll, timeout: 30000 });
})();

参考资料:

related question

jQuery example

【讨论】:

  • 据我了解,从客户端发出额外请求不会阻止之前的请求在负载均衡器处超时。我正在寻找的是 .Net 中的服务器端解决方案,它将在单个请求期间将数据发送回客户端,同时执行对第 3 方的上传调用。将数据发送回客户端应防止请求在负载均衡器处超时,因为正在传输数据。
  • 你可能是对的。我想上次我遇到类似的问题时,更多的是在网络级别应用了会话超时,所以只要你发送一些东西,交换机就不会让你超时。由于删除客户端解决方案相当容易,因此可能值得这样做进行测试。我期待看到其他人必须为您的问题保存什么。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-06-14
  • 1970-01-01
  • 1970-01-01
  • 2020-03-16
  • 2017-09-30
  • 1970-01-01
  • 2011-05-30
相关资源
最近更新 更多