我们正在 StackOverflow 上处理一个有趣的问题。

我们有一大堆“需要尽快完成”的小任务。一个例子是更新“相关问题”列表。我们过去所做的是将这些任务搭载到某些用户的页面加载上。

这从来都不是理想的,但并不真正引人注目。现在SO已经突破了1,000,000个问号,那些不幸的用户开始感受到了。

自然的解决方案是将这些任务实际推入后台。我正在考虑两种广泛的方法来做到这一点。

1.在 IIS 中作为自定义线程池/工作队列

基本上,我们旋转一些(非线程池, ,以免干扰 IIS)线程并让它们为我们正在推送的一些集合提供服务 功能 进入。

这里最大的优点是简单。我们不必担心整理任何内容,也不必确保某些外部服务已启动并做出响应。

我们还可以访问所有通用代码。

缺点是我们不应该使用后台线程。我所知道的反对意见都集中在 IIS 饥饿(如果您使用 ThreadPool)和线程随机死亡(由于 AppPool 回收)。

我们拥有现有的基础设施,可以使随机线程死亡不再是问题(基本上可以检测到任务已被放弃),并且限制线程数量(以及使用非线程池线程)也不困难。

我是否遗漏了 IIS 进程线程池/工作队列中的任何其他反对意见?

移至 StackOverflow, ,因为这里并没有真正解决这个问题。

2.作为服务

要么是某种第三方解决方案,要么是自定义解决方案。

基本上,我们会将跨流程边界的任务编组到某个服务,然后就忘记它。大概我们正在链接一些代码,或者限制为原始 SQL + 连接字符串。

优点是这是做到这一点的“正确方法”。

缺点是我们要么我们能做的事情非常有限,要么我们必须制定一些系统来保持此服务与我们的代码库同步。我们还需要以某种方式连接所有的监控和错误记录,这是通过“在 IIS 中”选项免费获得的。

该服务方法还有其他好处或问题吗?

简而言之,是否存在无法预见和无法克服的问题导致方法#1 无法实施?如果是,我们应该为方法#2 寻找任何好的第三方服务吗?

有帮助吗?

解决方案

几周前我问了一个 类似的问题 这样。在螺母外壳中,我一段时间以来一直是开发Windows服务。我将使用nservicebus(基本上是封面下的MSMQ)来从我的Web应用程序到服务的元帅请求。我曾经使用WCF,但是通过WCF进行分布式交易才能正常工作,这似乎是屁股的痛苦。 nservicebus做到了这一点,我可以提交数据并在交易中创建任务,而不必担心当时我的服务是否启动并运行。作为一个简单的示例,如果我需要发送电子邮件(例如注册电子邮件),我将创建用户帐户,并向我的Windows服务(发送电子邮件发送)发出信号。服务方面的消息处理程序将相应地接收消息并处理。

由于已经发布了ASP .NET 4.0和AppFabric,因此上述机制有许多可行的替代方法。回到我上面提到的问题时,我们现在拥有AppFabric的Appinitialize(通过Net.Pipe)以及ASP .NET 4.0的自动启动功能,使Windows服务作为Web Apps成为可行的替代方案。我现在开始这样做的原因有很多(部署最大的部署不再是屁股的痛苦):

  1. 您可以通过您的服务开发Web UI(因为它作为Web应用程序运行)。看到运行时发生的事情非常有用。
  2. 您的Web应用程序部署模型将适用于您的服务应用程序。
  3. IIS为处理应用程序故障提供了一些整洁的功能(在某些方面与Windows服务相似)。
  4. Web开发人员非常熟悉开发Web应用程序(自然),大多数人在开发Windows服务时对最佳实践的了解不多。
  5. 它提供了多种替代方案,用于公开其他应用程序消耗的API。

如果您走这条路线(请原谅我从我的原始帖子中复制和粘贴),我肯定会考虑在单独的Web应用程序中运行背景逻辑。有很多原因:

  1. 安全. 。 UI可能有不同的安全模型,显示有关运行背景过程的信息。除了OPS团队以外,我不想将此UI曝光。此外,Web应用程序可以作为具有升高权限的不同用户运行。
  2. 维护. 。能够在不使用前端网站影响用户的情况下部署托管背景过程的应用程序的更改将是很棒的。
  3. 表现. 。将应用程序与主站点处理用户请求分开,这意味着背景线程不会降低IIS处理传入请求队列的能力。此外,如果需要,可以将处理背景任务的应用程序处理到单独的服务器。

这样做回到了安排方面。 WCF,NServiceBus/RabbitMQ/ActiveMQ等,Vanilla MSMQ,Restful API(Think MVC)都是所有选项。如果您使用的是Windows Workflow 4.0,则可以公开Web应用程序可以消耗的主机端点。

对我来说,网络托管方法对我来说仍然是新的,只有时间才能说明这是否是正确的选择。到目前为止还不错。顺便说一句,如果您不想使用AppFabric(我不能因为不支持Windows Server Web版本而无法使用AppFabric),则GU的帖子中提到的自动启动功能非常有效。不过,远离applicationhost.config文件,该帖子中的所有内容都可以通过IIS控制台(主服务器级别上的配置编辑器)进行设置。

注意:我最初在此消息中发布了更多链接,但是可惜,这是我的第一篇文章,仅支持一个链接!基本上还有另外两个,以使他们获得Google“ Windows Services的死亡……Love Live Appfabric!”和“自动启动-ASP-NET-applications”。对于那个很抱歉。

其他提示

Windows实际上是运行背景服务的第三种方法,并且在UNIX世界中非常普遍。第三种方式是 CRON 运行您的基础设施的工作。在窗户中,这被称为 task scheduler 并且对于按计划运行代码非常常见。要使用此功能,您将创建一个按照预定时间表执行的命令行应用程序。这样做的优点是,您不必担心该过程是否像服务一样启动并运行,因为如果出于某种原因失败,它将下次启动。

至于编制特定任务,您实际上只需要将这些任务存储在持续的二进制存储中即可。直到命令行应用程序将它们从存储空间中摘下并执行。我过去曾使用Cassandra数据库作为会话状态提供商来为Cassandra数据库中的特定用户填充背景任务,然后让命令行挑选它们并为用户执行它们。

这可能不是典型的编组解决方案,但是对我来说很好,事实证明这是一个非常优雅的解决方案,因为预定的任务在关机上幸存下来,网络问题和任何机器都可以执行该任务,因为它是集中的存储。

无耻的促销活动,但这是我的项目,我简要详细介绍的解决方案是我创建该项目的原因:http://github.com/managedfusion/fluentcassandra/

Cron + Web应用程序

这是经过战斗的设计 水平缩放 与您的网络农场一起确保您正在使用 网络技术堆栈 你已经知道了。

以下是它的工作方式:

  1. 在您的Web应用程序中创建控制器/操作以处理计划的背景任务。按照惯例,我通常叫我 http://mydomain.com/system/cron.
  2. 为了安全性,该操作应锁定到本地网络上的身份验证的IP地址。
  3. 在单独的机器上,安装 wget 并设置a 计划的任务 要使WGET从步骤1获取资源。您可以根据需要进行任务运行(我通常选择30秒)。不要忘记将适当的cookie参数传递给WGET,以便对您的Web应用程序进行身份验证。
  4. 对于冗余,您还可以在第二台计算机上安装第二个计划的WGET。

哇!现在,您有一条路线每30秒被称为每30秒。而且,如果请求需要5分钟的时间来处理,则没有人会在意,因为它不是用户页面请求的一部分。

cron 动作最终看起来很简单:他有一个可以在一定频率上执行的方法列表。当请求进来时,他会看到是否有需要执行的方法并调用适当的方法。 这意味着您可以控制数据库中的时间表, ,您可能已经有许多其他重要的配置数据为您的网站。

更重要的是(对您来说),这意味着您不必按固定的时间表来调用您的工作。您可以编写要确定何时执行方法的任何逻辑。

利弊

优点
  • 您已经非常擅长编写ASP.NET MVC代码,因此,这使您可以在中写下背景任务 同一平台 您将其余的解决方案写入其中。
  • 任务在与您的Web应用程序相同的上下文中运行,因此您可以 共享缓存 并利用 助手方法 那已经存在。
  • 如果您有wget提取 负载平衡 URI,那么您的背景任务现在也是负载平衡的。
  • 同时部署 - 您不必担心将Web应用程序与背景任务逻辑同步,因为它们都处于同一部署中。
缺点
  • 多年来,一些人告诉我,这种设计是“高度耦合”的,但是当被压迫时,他们无法阐明为什么这是一件坏事。

注意:如果有任何疑问或疑虑, 请添加评论. 。我很高兴详细说明。

我已经尝试并使用了几乎所有可能在当前应用程序中执行此操作的方法。我开始做与您当前相同的事情,请回到用户请求以填写数据然后缓存其后期的请求。我意识到这也是一个坏主意(尤其是当您扩展到多个网络服务器时,更多的用户会受到打击)。

我也有一个计划的作业,可以在ASP.NET应用程序中击中URL - 这是一个不错的解决方案,但它开始分解您将您扩展到1个Web服务器的那一刻。

目前,我使用两种不同的方法,均使用Quartz.net,这是一个很棒的小库。第一个是用ASP.NET运行程序进行的Quartz.net,它是在global.asax中设置的,每隔几分钟运行。我用它来更新频段中的ASP.NET缓存,这是作为ASP.NET的一部分运行的唯一原因。

第二个是我写了一个库来包装Quartz.net称为Daemonmaster - 它使将DLL放入目录并将其运行在Windows服务中。我发现它有助于避免使用Windows服务的一些烦人的部分,并清理Quartz.net API。通过守护程序的服务有两种不同的口味,第一个是需要每晚或每个X人罚款的工作。其他作业根据来自ASP.NET应用程序的数据从队列中工作。 ASP.NET应用程序在RabbitMQ上删除JSON对象,然后将RABBITMQ Poll RabbitMQ删除,然后处理数据。

基于此,我建议您使用Windows服务(并查看Daemonmaster),如果需要,请使用RabbitMQ之类的队列将数据从ASP.NET应用程序传递到服务 - 它在所有这些解决方案中都可以使用最好。如果您正在加载缓存,则在ASP.NET中运行是有道理的,否则我认为它不会。

我会以正确的方式进行操作,并运行一个监视“队列”的Windows服务。我之所以说“队列”,是因为带有MSMQ的编程类似于粘在您的眼球中。

我爱上了简单的 延迟:: Job 在轨道上,很容易在.NET中完成类似的事情。

基本上您添加了任何类型的 SomethingOperation (有一个 Perform() 方法)。然后,仅序列化相关参数,将其优先考虑,某种默认的重试行为,然后将其塞入数据库中。

您的服务只会监视这一点,并在队列中工作。

我们对服务总线 /消息队列 /服务方法感到非常满意。基本架构是这样。

网站向队列发送消息

bus.Send(new ProjectApproved()); // returns immediately

Windows Service在自己的时间内接收和处理消息

public class DoesSomethingAwesome : ConsumerOf<ProjectApproved>
{
   public void Consume(ProjectApproved Message)
   {
      // Do something "offline"
   }
}

优点是,前端服务也没有延迟,也没有连接用户。 Windows服务可以关闭,并在不中断到主站点的情况下进行升级。再加上 非常快.

如果您无法将所有数据存储在消息中,则可以始终将其存储并以后再检索。我建议使用文档存储机制,例如: 拉文 或者 mongodb 在没有更改的情况下存储您的课程非常直接。

网站向队列发送消息

// Save your object
store.Save(completeProject);

// Send a message indicating its ready to be processed
bus.Send(new ProjectApproved() { ProjectId = completeProject.Id });

Windows Service在自己的时间内接收和处理消息

public class DoesSomethingAwesome : ConsumerOf<ProjectApproved>
{
   public void Consume(ProjectApproved Message)
   {
      // Retrieve your object back
      var completeProject = store.Get(Message.ProjectId);
   }
}

为了使事情变得简单:我们使用: 犀牛ESB顶架. 。该配置非常简单,并将其设置为现有应用程序,事实证明花费了很少的时间。

我很好奇为什么两者的组合不是一个可行的选择。现在,您可以在页面视图上触发作业,其中一些不幸的SAP被卡住了,等待10秒才能出现。至少这是我对您当前方法的理解。

但是,随着网站的增长,这些工作需要越来越长的时间来运行,您不想在网站上的用户体验脱轨。整天都没有(甚至很多)不幸的用户(也许很多),所以现在您正在考虑在后台安排作业。

我不明白为什么定期进行背景作业无法模仿访客。现在,我不是Windows程序员,但是在Linux世界中,我会设置一个定期时间运行的CRON作业,并且它将具有2行代码。

#!/bin/bash
wget -O /dev/null http://stackoverflow.com/specially_crafted_url

它结合了两个系统的优点。它在后台完成。它不会影响用户。它仍然使用页面视图来启动工作。我以前看过这种方法。它往往是旧的简单方式与走路更复杂的方式之间的中间立场。

更新

我认为您可以通过在Web服务器上运行工作跑步者本身来解决负载平衡问题。跑步者将URL从工作队列中提取出来,然后像这样运行:

wget -O /dev/null http://localhost/specially_crafted_url

由于工作/消息队列的性质,这些工作将在工作跑步者中均匀分布,这意味着最终在您的Web服务器中分发了专门的_cructed_url。

我认为使用纯服务方法的骗局是,您将代码分散到服务中并远离核心应用程序。

这是我们在大型背景非时间敏感的工作中所做的工作,这可以使代码保持在一起并简化服务:

  1. 创建一个作业队列(无论是内存还是DB,无论工作类型都需要什么持久性)
  2. 创建将执行排队作业的Web服务
  3. Dead Simple Service应用程序以指定的间隔调用Web服务,将所有复杂的内容(作业检索和执行)留在核心代码库中的Web服务。

更简单,只需在控制台应用程序中调用,然后使用任务调度程序或VisualCron将其转换为“服务”。

我喜欢Topshelf。保持简单性,但仍然以适当的方式作为Windows服务进行操作。基本上创建一个控制台应用程序,添加约15-20行代码,然后作为服务安装。

http://code.google.com/p/topshelf/

如何在Web服务器上运行一个非常简单的Windows服务,并定期击中执行其他任务的维护URL。在任何给定的请求中,它都会油门进行多少工作。

我将在这里限制明显的趋势,并建议使用IN-IIS模型。我自己使用了它,它的工作原理非常好。实现一个体面的线程池类并不难(多年来,我扩展了线程池类以支持动态创建和破坏线程,重试作业等)。优点是:

  • 没有外部服务可以监视
  • 实施的简单性:没有交叉进程编组,没有高级工作监控
  • 您仍在IIS过程中,因此您可以执行所有通常的记录等等(无需多个日志文件)
  • 非常简化的部署(更新服务时,您必须停止服务,复制文件,启动服务 - 这是您通常更新到网站代码的补充)

我认为,IN-IIS解决方案只是从将作品带回随机页面视图中的“下一步”。

雷克斯 很好。甚至 kthxbye 如果您需要在完成后通知结果值。

redis/ruby均基于tho。

老实说,如果您正在采用基于服务的方法,那么它确实不需要与您当前的平台超级整合,我认为这是一个加号。我希望它可以是一个设置和验证系统,可以运行(通过某种形式的监视)和完整的作业。我不确定它是否只能在同一平台上运行,因为它只是更新/修改数据库信息。

可以肯定的是,如果您耕种此类实体,尤其是因为您似乎正在处理线程问题,那么您可能会以更少的价格摆脱更多东西。两个都 雷克斯kthxbye 将处理移到单独的过程中,以允许操作系统处理并发。

雷克斯

kthxbye

我将使用托管的WCF服务收听MSMQ队列。

专家

  • 射击并忘记Web应用程序的单程消息

  • MSMQ/WCF节流和重试

  • 保证交货; D

  • 死信管理

  • 分布式处理

  • IS / MSMQ激活

骗子

  • MSMQ(还没有死...)

WCF中的MSMQ功能使使用MSMQ非常好。是的,您会在配置上流血,但好处将超过牺牲。

开发Web应用程序时,我已经遇到了几次。我们一直在通过创建执行任务的Windows控制台应用程序来解决它,并创建一个计划的任务,该任务经常运行以实际完成任务。

您可以使用RX和类似的内容分流到背景线程(或许多背景线程)上:

var scheduler = new EventLoopScheduler( SchedulerThreadName );
_workToDo = new Subject<Action>();
var queueSubscription = _workToDo.ObserveOn( scheduler ).Subscribe( work => work() );
_cleanup = new CompositeDisposable( queueSubscription, scheduler );

使用:

var work = () => { ... };
_workToDo.OnNext( work ); // Can also put on error / on complete in here

在一个班级中托管所有的东西,只有一个(又称单身人士,但要正确地做) - 使用ioc容器来确定生活方式)。

您可以通过编写自定义调度程序来代替使用EventLoopscheduler(运行单个线程)来控制线程池等的大小。

我已经实施了几次这种类型的事情。在Windows上,我设置了一个python命令行程序,该程序在不同时间进行操作。该程序还在端口上公开了XMLRPC接口。然后,计划的任务作业每分钟运行,并查询XMLRPC接口。如果他们不升起,它将试图启动它们。如果不能,它会给我发电子邮件。

优势在于,运行的工作不是cron或时间表约束。我的过程工作每秒运行一次,但是在开始新工作之间会等待越来越长的时间,具体取决于它是否有工作。同样,它可以根据结果智能地采用。有500个错误?延迟很长吗?做其他事情。通知另一个服务。等等。

并且同一系统在UNIX上起作用,并进行了少量修改。

我自己没有答案 一次在播客上讨论一次.

Spolsky:我注意到您在博客上问的一个问题之一是,您应该如何处理一般的维护重复任务?

阿特伍德:是的。

斯波尔斯基:这是一个公平的特征吗?每个网站都有一些您不想在加载网页时执行的任务,但是您想通过某种复发进行执行。

Atwood:是的,背景任务是有些事情。

Spolsky:是的,那你怎么了?

Atwood:好吧,我最初在Twitter上问,因为我只是想要一些重量的东西。我真的不想喜欢写Windows服务。我觉得那是不合时间的。再加上实际完成工作的代码实际上是一个网页,因为对我而言,这是网站上的合乎逻辑的工作单位是网页。因此,这真的就像我们正在回到网站一样,就像网站上的另一个请求一样,所以我将其视为应该保持在线的东西,以及我们在Twitter上推荐的小方法本质上是要在固定的到期时添加某些内容,然后您会回电,因此当它到期时,它调用了一个可以完成工作的功能,然后将其添加到同一到期的缓存中。因此,这有点了,也许“贫民窟”是正确的词。

任务队列Java API概述

任务概念
在App Engine背景处理中,任务是对一小部分工作单位的完整描述。此描述由两个部分组成:

  • 参数化任务的数据有效负载。
  • 执行任务的代码。

任务作为离线网络挂钩
幸运的是,Internet已经以HTTP请求及其响应的形式提供了这样的解决方案。数据有效载荷是HTTP请求的内容,例如Web表单变量,XML,JSON或编码二进制数据。代码参考是URL本身;实际代码是服务器在准备响应时执行的任何逻辑。

两者都做

在问题路径中添加一个可选参数,该参数可以完成您当前正在根据用户请求进行的工作:

为大型网站提供后台任务服务

创建一个在每台服务器上运行的控制台应用程序,打开 IIS 日志共享二进制文件并将其读取到文件的当前末尾。使用文件系统观察程序或定时间隔在 IIS 刷新日志时向前读取以收集更新。

使用此信息来确定当前查看过哪些页面。

使用解析日志中的页面 url 通过 webclient 对象调用 localhost 上 url 的“extrastuff”版本。

添加一些代码以在每个日志周期结束时切换文件或在每个日志周期重新启动进程。

许可以下: CC-BY-SA归因
scroll top