2

我正在运行一个 Windows Azure Web 角色,在大多数情况下,该角色接收的流量非常低,但有一些(可预见的)事件可能导致必须完成大量后台工作。后台工作由许多数据库调用 (Azure SQL) 和对外部 Web 服务的 HTTP 调用组成,因此它并不是真正的 CPU 密集型工作,但它需要大量等待数据库或 Web 服务响应的线程。后台工作由对 Web 角色的正常 HTTP 请求触发。

我看到有两种选择来协调这一点,但我不确定哪个更好。

  • 选项 1,线程:当后台工作请求进来时,Web 角色会根据需要启动尽可能多的线程(或将各个工作项排队到线程池中)。在这个选项中,我会在繁重的工作负载期间配置一个更大的实例,因为这些线程可能需要大量内存。
  • 选项 2,自调用:当后台工作的请求进来时,接收它的 Web 角色会为每一项后台工作向自己生成一个 HTTP 请求。在此选项中,我可以配置多个 Web 角色实例,因为 Windows Azure 的负载平衡器会在实例之间平衡 HTTP 请求。

选项 1 稍微简单一些,但它的缺点是只有一个实例可以处理后台工作。如果我希望多个 Azure 实例参与后台工作,除了将 HTTP 请求从角色发送到自身之外,我看不到任何其他选项,以便负载均衡器可以将一些工作委派给其他实例。

也许还有其他选择?

编辑:关于选项 2 的更多想法:当后台工作请求进来时,接收它的实例会将要完成的工作保存在某种队列中(Windows Azure 队列或一些作为任务工作的 SQL 表队列)。然后,它会向自己生成大量 HTTP 请求,以便负载均衡器“激活”所有角色实例。然后每个实例从队列中取出一个任务并执行该任务,然后获取下一个任务等,直到所有任务完成。这就像偶尔将网络角色用作工作者角色一样

我知道这种方法有一种臭味(滥用 Web 角色作为工作角色,对同一个 Web 角色的 HTTP 请求),但我没有看到真正的缺点。

编辑 2:我看到我应该详细说明应用程序的确切情况:

该应用程序需要一直执行一些小任务。这些任务通常不会超过 1-10 秒,而且它们不需要大量的 CPU 工作。在正常的日子里,我们只有 50-100 个任务要完成,但在“特殊日子”(新年就是其中之一),他们可能会进入几个 10000 个任务,这些任务必须在 1-2 内完成小时窗口。这些任务以 Web 角色完成,我们有一个Cron 作业,它每分钟启动一次任务。因此,Web 角色每分钟都会收到处理新任务的请求,因此它会检查必须处理哪些任务,将它们添加到某种队列中(目前它是一个带有 UPDATE 和 OUTPUT INSERTED 的 SQL 表,但我们打算切换有时到 Azure 队列)。目前,同一个实例处理任务排队后立即,但这不会扩展,因为几个 10'000 任务的串行处理需要太长时间。这就是为什么我们正在寻找一种机制来将事件“任务可用”从初始实例广播到其他实例的原因。

4

4 回答 4

3

您是否考虑过使用队列来分配工作?您可以将需要处理的“任务”放入队列中,然后将工作分配给多个工作进程。

我在方法 1 中看到的问题是我将其视为“Scale Up”模式而不是“Scale Out”模式。通过部署许多小型 VM 实例而不是一个大型实例将为您提供更多的可扩展性 + 可用性恕我直言。此外,您提到您的工作不是 CPU 密集型的。如果您考虑 X-Small 实例,以 1 个小型实例(0.12 美元/小时)的成本,您可以部署 6 个 X-Small 实例(0.02 美元/小时),同样,以 1 个大型实例(0.48 美元)的成本,您可以部署 24 个X-小型实例。

此外,在“横向扩展”模式的情况下,您只需添加或删除实例就可以轻松扩展。在“Scale Up”(或“Scale Down”)模式的情况下,因为您正在更改 VM 大小,您最终将重新部署包。

对不起,如果我有点切线:) 希望这会有所帮助。

于 2012-10-15T08:42:26.033 回答
2

[添加作为单独的答案以避免 SO 告诉我切换到聊天模式 + 绕过评论长度限制] & 大声思考:)

我明白你的意思了。基本上通过 HTTP 请求,您可以将要处理的新任务的可用性广播到其他实例。

因此,如果我理解正确,当一个实例接收到要处理的任务的请求时,它会将该请求推送到某种队列中(就像你提到的那样,它可能是 Windows Azure 队列 [我个人实际上更喜欢那个] 或 SQL Azure 数据库[不喜欢那样,因为您必须实现自己的消息锁定算法])然后将消息广播到需要完成某些工作的所有实例。剩余的实例(或者可能是广播它的实例)然后可以查看它们是否可以自由处理该任务。一个实例取决于其可用性,然后可以从队列中获取任务并开始处理该任务。

假设您使用了 Windows Azure 队列,当一个实例获取消息时,它会在一段时间内立即对其他实例不可用(Azure 队列的可见性超时期限),从而避免重复处理任务。如果任务处理成功,则处理该任务的实例可以删除该消息。

如果由于某种原因,该任务没有被处理,它会在可见性超时期限到期后自动重新出现在队列中。然而,这导致了另一个问题。由于您的实例基于触发器(生成 HTTP 请求)而不是轮询来查找任务,您将如何确保所有任务都完成?假设您只处理一个任务和一个任务并且由于您没有收到处理第二个任务的请求而失败,那么第一个任务将永远不会再被处理。显然它不会在实际情况下发生,但您可能需要考虑一下。

这有意义吗?

于 2012-10-15T10:21:48.040 回答
2

我同意 Gaurav 和其他人考虑使用 Azure Queue 选项之一。这确实是一种方便的模式,可以清晰地分离关注点,同时还可以平滑负载。

这种基本的以队列为中心的工作流 (QCW) 模式在处理 Web 角色的 HTTP 请求(触发工作的机制,显然是通过调用 wget 的 cron 作业完成)时将工作请求放置在队列中。然后,Web 角色中的 IIS Web 服务器继续做它最擅长的事情:处理 HTTP 请求。它不需要负载均衡器的任何支持。

Web 角色需要尽快接受请求(然后为每个请求排入队列),但出队部分是拉取的,因此可以轻松调整负载以适应可用容量(或为负载调整容量!这就是云!)。您可以选择一次处理一个、一次处理两个或一次处理 N 个:无论您的测试(调整大小练习)告诉您什么都适合您部署的 VM 大小。

您可能也知道,Web 角色上的 RoleEntryPoint::Run 方法也可以实现为连续工作。Web 角色的默认实现基本上只是永远休眠,但您可以实现无限循环来查询队列以删除工作并处理它(并且不要忘记在队列中没有可用消息时休眠!失败这样做会导致资金泄漏,并可能使您受到限制)。正如 Gaurav 所提到的,在稳健地实施这种 QCW 模式时还有一些其他考虑因素(如果我的节点失败,或者如果我的代码中有错误(“毒药”)消息、错误等会发生什么),但您的用例没有似乎对此过于担心,因为 cron 工作的下一次踢球显然会解释任何(罕见,

将队列中的项目与处理队列中的项目分离确实是一个合乎逻辑的设计点。我的意思是您可以随时更改它并将处理端(从队列中拉出的代码)移动到另一个应用程序层(服务层)而不破坏基本设计的任何部分。这提供了很大的灵活性。大多数时候,您甚至可以在单个 Web 角色节点上运行所有内容(或者两个,如果您需要 SLA - 根据您的一些评论不确定您是否这样做)(两层),然后根据需要通过添加一堆处理虚拟机,比如过年。

处理节点的数量也可以根据来自环境的信号动态调整 - 例如,如果队列长度正在增长或超过某个阈值,则添加更多处理节点。这就是云,这台机器可以完全自动化。

现在越来越投机,因为我对您的应用程序不太了解...

通过使用前面提到的 Run 方法,您也可以消除 cron 作业并在该无限循环中完成该工作;这当然取决于 cron 调度的复杂性。或者,您甚至可以通过让您的 cron 作业将工作请求项直接放在队列中(可能使用SDK 之一)来消除整个 Web 层(Web 角色)。您仍然需要代码来处理请求,这当然仍然可以是您的 Web 角色,但此时可以很容易地使用工作角色。

于 2012-10-16T01:28:38.873 回答
1

我肯定会选择横向扩展解决方案:更简单、更易于管理且定价更好。另外,在部署失败的情况下,您的停机风险较小(当然,故障和升级域的机制应该涵盖这一点,但尽管如此)。因此,就此而言,我完全支持 Gaurav!

于 2012-10-15T10:38:57.190 回答