Azure Web角色中的后台线程

问题描述:

可以通过WebRole.cs OnStart()方法运行线程,以便我们可以通过aspx页面访问它以执行后台工作? 我知道正确的做法是使用工人角色,但我希望尽可能降低运行成本。Azure Web角色中的后台线程

这个想法是创建一个线程,总是会运行并等待工作,例如,如果我想进行阻塞操作就像发送电子邮件我会使用线程给出SendEmail方法,是否有可能去做?如果是这样,你能否给我提供一些可以指引我正确方向的例子?

+0

这将如何降低运行成本?你想要什么不同于单实例单一并发服务? – Paparazzi

+3

@Blam - 在Web角色中运行后台任务允许将操作组合成一组VM实例。这与将背景操作放在单独的一组角色实例中形成鲜明对比。组合成一个角色将节省成本,因为每个角色必须至少有一个实例正在运行。对于小批量网站,这是一个非常节省成本的架构。如果存在背景任务挨饿网站的风险,或者需要分别缩放前端和后台任务(或者需要不同的VM大小),则值得考虑转向单独的角色。 –

+0

@DavidMakogon谢谢我不知道一个工作者角色需要一个单独的实例。我没有试图回答这个问题。 +1我从这个问题中学到了很多东西。 – Paparazzi

我会suggets一个解决方案,是从莱昂和大卫的解决方案不同:

  • 大卫的解决方案是确定的,但不是弹性的。实例/进程在处理任务时脱机了什么?
  • Leon的解决方案大多适用于预定作业,但发送电子邮件并不总是按计划安排的(也许您想在有人在您的应用程序中注册时发送电子邮件)。

的另一种选择,你应该看看使用的是Windows Azure存储队列(他们很便宜)在这种情况下:

  • Web应用程序:将消息发送到队列(如“发送电子邮件to [email protected]')
  • WebRole.cs:在启动实例时产生一个新线程,让它侦听来自该队列的消息。每当消息到达时,处理它。如果成功,则从队列中删除消息。

该解决方案有很多优点。 WebRole.cs运行在与Web应用程序不同的进程中,因此对请求线程没有影响。除此之外,如果发送邮件因任何原因失败,邮件将保留在队列中并在下次处理。如果应用程序或进程崩溃,这将确保您不会丢失任何要执行的任务。

下面是一个让你开始的例子。请注意,如果您希望生产准备就绪(重试策略,异常处理,退避轮询等),则需要改进此代码:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using Microsoft.WindowsAzure; 
using Microsoft.WindowsAzure.Diagnostics; 
using Microsoft.WindowsAzure.ServiceRuntime; 
using Microsoft.WindowsAzure.StorageClient; 
using System.Threading.Tasks; 

namespace MvcWebRole1 
{ 
    public class WebRole : RoleEntryPoint 
    { 
     public override bool OnStart() 
     { 
      Task.Factory.StartNew(InitializeQueueListener); 
      return base.OnStart(); 
     } 

     private void InitializeQueueListener() 
     { 
      Microsoft.WindowsAzure.CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) => 
      { 
       configSetter(Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.GetConfigurationSettingValue(configName)); 
      }); 


      var storageAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString"); 
      var queueStorage = storageAccount.CreateCloudQueueClient(); 
      var queue = queueStorage.GetQueueReference("myqueue"); 
      queue.CreateIfNotExist(); 

      while (true) 
      { 
       CloudQueueMessage msg = queue.GetMessage(); 
       if (msg != null) 
       { 
        // DO SOMETHING HERE 
        queue.DeleteMessage(msg); 
       } 
       else 
       { 
        System.Threading.Thread.Sleep(1000); 
       } 
      } 
     } 
    } 
} 
+0

似乎是更好的解决方案,我愿意执行的工作类型,无论如何,我打算让队列与内部应用程序“交谈”,所以它不会那么昂贵......您可以进一步详细了解“在启动实例时产生一个新的线程并让它监听来自该队列的消息”? **如果你可以给我一个例子,或者指向一个样例,它解释了在WebRole.cs **中启动一个监听线程所涉及的线程部分,那将是非常好的。谢谢 – ToinoBiclas

+0

同意 - 队列非常有弹性。我的意图是指出创建线程的可行性(这并不总是显而易见的)。 –

+0

@Sandrino让我知道如果这是内联与您的解决方案...在WebRole.OnStart()'新线程(新ThreadStart(ClassListeningToQueue.Method0)''创建一个线程'然后在Method0内写入一个无限循环或条件循环它只通过WebRole.OnStop()'while(true){try {CloudQueueMessage msg = queue.GetMessage(); if(msg!= null){''finnaly根据msg中的值执行任务,例如我可以有一个字段指出调用的方法和参数,在主线程中调用其他线程是否安全? – ToinoBiclas

我发现这个时候我一直在寻找的“蔚蓝计划任务”:http://www.ronaldwidha.net/2011/02/23/cron-job-on-azure-using-scheduled-task-on-a-web-role-to-replace-azure-worker-role-for-background-job/

看起来正是你要寻找的。

+0

您指出的例子是用于运行预定作业,很像Linux中的crontabs。我愿意做一个等待被发信号的线程。 – ToinoBiclas

+0

这只是指出,完全可以做这些事情。我想你可以用一点创意想出一个解决方案。 –

绝对可以创建一个线程(或很多)。 Web角色基本上是Windows 2008 Server。您不需要单独的工作人员角色来设置后台任务。当然,你的可以有一个单独的辅助角色,这将允许你扩展这些实例,而不依赖于你的Web角色实例。这是您需要平衡性能/扩展与成本之间的关系。

+0

你能给我一些关于如何做到这一点的细节?如果我已经在WebRole:RoleEntryPoint中声明了,我怎样才能从一个aspx网页访问线程变量? – ToinoBiclas

+2

如果您需要与aspx页面中的线程交互,为什么不从global.asax启动线程? RoleEntryPoint将在单独的AppDomain中运行,因此您不会在应用程序和线程之间进行交互。 –

+0

老实说从未想过这件事。我将尝试使用global.asax中声明的线程池进行一些测试。 – ToinoBiclas