【问题标题】:How to communicate with a background task?如何与后台任务进行通信?
【发布时间】:2011-07-14 08:56:37
【问题描述】:

我正在开发一个与 Web 服务通信的应用程序。客户端应用程序 (silverlight-4.0) 将调用 Web 服务并触发一个长时间运行的任务。因为任务完成需要一些时间,所以它在单独的线程中执行。 (使用 System.Threading.Tasks.Task.Factory.StartNew() 创建单独的任务。)启动任务后,服务调用将返回一个 ID 并完成连接。
这个 ID 应该是我需要用来识别任务的东西,以便我可以与之通信。

下一次调用,可能使用另一个连接,检查任务是否已经完成。为此,ID 是呼叫的一部分。在服务器上,它现在需要检查任务是否仍在运行或是否已完成。如何再次找到此任务?


服务在 Azure 上运行,由于负载平衡,第二次调用可能在完全不同的系统上。在我看来,这无法做到,但话又说回来......
这个Q与this Q有关。

【问题讨论】:

    标签: asp.net c#-4.0 azure asmx


    【解决方案1】:

    让任务在表存储/appfabric 缓存中报告其状态。然后,当有人轮询状态时,只需从所使用的持久性机制中读取任务 ID X 的相应状态即可。

    【讨论】:

    • +1 两个答案 - 同时具有相同的想法。在极端情况下——客户端和服务器之间需要非常紧密的耦合——那么你也可以使用粘性会话模式——dunnry.com/blog/2010/10/14/…——但这不适用于简单的情况(IMO)
    • 老实说,maartenba 比我快 4 秒左右。
    • 使用表存储/appfabric 缓存很诱人,并且可以简化大部分轮询。但是大卫斯蒂尔建议的工人角色听起来也很有趣......
    • 另见这个问题(它与 ajax 相关,但我相信你可以适应它)一些代码:stackoverflow.com/questions/6184752/…
    • 工作者角色实际上是一个很好的解决方案,但为了能够将其扩展到“无限”,您还必须实施某种使用共享存储的轮询策略。
    【解决方案2】:

    这样做的正确方法是使用基于队列的通信。原因是可扩展性。您希望服务的“实例”接收请求,并且希望“实例”将结果返回给客户端,对吗?

    您可以快速浏览一下我的一篇关于 AppFabric Queues 的博客文章,但它们对于这个来说太庞大了。这是我的做法:

    创建一个 WorkerRequest 类,看起来像这样

    public class WorkerRequest {
       string clientId;
       MyTaskEnum taskToPerform;
    }
    

    写入队列存储,(在我的生产代码中,我使用了一个包装器,我还没有写过博客,但计划这样做:)),添加请求。

    让一个工作线程监听这个队列,当收到请求时,产生一个新线程来完成它。完成后,将您的任务和客户端 ID 作为密钥写入 表存储。这样您就可以随时检查状态(对表的简单 /GET/ 请求)+ 您已经解决了解耦和可扩展性问题。

    希望对你有帮助。

    更新:想再解释一下,所以我决定更新帖子=)

    您可以在“Web 角色”中创建 WCF Web 服务,这就是我会做的。我刚才blogged about它。在同一角色中,您创建了一个 Worker。你可以通过一个实现RoleEntryPoint 的类来做到这一点。此类(位于 Microsoft.WindowsAzure.ServiceRuntime 中)如下所示:

      public abstract class RoleEntryPoint
      {
        public virtual bool OnStart()
        {
          return true;
        }
    
        public virtual void Run()
        {
          Thread.Sleep(-1);
        }
    
        public virtual void OnStop()
        {
        }
      }
    

    您只需在 Run 中实现一个 while(true) 循环,它会询问队列是否有任何新消息要处理。当收到这样的消息时不要产生新的,只需处理它。如果要扩展它,可以通过添加新实例进行扩展。现在显然,这可能会很昂贵,因此实际上生成一个新线程是明智的,但仅限于某个限制,例如最多 5 个线程。如果您的池中没有线程,则将消息返回到队列(您需要在完成消息后调用Complete(),否则它不一定会被删除)。它稍后会被拾起,或者由其他工人拾起。

    所以,当工作线程完成时,将结果写入表存储就完成了。

    【讨论】:

      【解决方案3】:

      任务只需要保存在某个已完成的地方,并在预期结果时存储结果。

      您可以将带有任务 ID 的完成存储在 Table Storage 或 SQL Azure 中,只要有可用的地方。后续轮询是否完成可以只检查这个存储并返回是否完成。

      解决此问题的另一种方法是让长时间运行的任务以工作者角色运行。如果此辅助角色暴露了一个内部端点,那么任何 Web 角色都可以询问辅助角色是否已完成。

      【讨论】:

      • 工人角色?嗯……有趣的概念。还没想过这些。
      • 如果任务运行时间很长,但 CPU 或 IO 不太密集,那么您可以在 Web 角色的单独线程中运行它们并节省资金。如果他们是繁重的任务,那么最好将他们推到单独的工作角色中,以免对您的网络角色产生不利影响。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多