编排Windows Azure web角色以应对偶尔的高工作负载



我正在运行一个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角色滥用为工作角色,对同一web角色的HTTP请求),但我看不出真正的缺点。

第2版:我觉得我应该详细说明一下应用程序的具体情况:

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

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

我在方法1中看到的问题是,我认为这是一种"向上扩展"模式,而不是"向外扩展"模式。通过部署许多小型虚拟机实例而不是一个大型实例,将为您提供更多的可扩展性和可用性IMHO。此外,您提到您的工作不是CPU密集型的。如果您考虑X-Small实例,以1个Small实例(0.12美元/小时)的成本,您可以部署6个X-Small实例(0.02美元/小时,同样以1个Large实例(0.48美元)的成本部署24个X-Small示例。

此外,在"横向扩展"模式的情况下,只需添加或删除实例就可以轻松进行扩展。在"按比例放大"(或"按比例缩小")模式的情况下,由于您正在更改VM大小,您最终将重新部署包。

对不起,如果我说得有点离题的话:)希望这能有所帮助。

我同意Gaurav和其他人考虑Azure队列选项之一。这确实是一种非常方便的模式,可以干净地分离关注点,同时还可以平滑负载。

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

Web角色需要尽快接受请求(然后为每个请求排队一条消息),但出列部分是pull,因此可以很容易地根据可用容量(或根据负载调整的容量!这就是云!)调整负载。您可以选择一次处理一个,一次处理两个,或者一次处理N:无论您的测试(大小调整练习)告诉您适合您部署的VM大小。

您可能也知道,Web角色上的RoleEntryPoint::Run方法也可以实现为连续工作。Web角色上的默认实现基本上只是永远休眠,但您可以实现一个无限循环来查询队列以删除工作并处理它(当队列中没有可用消息时,不要忘记休眠!如果不这样做,将导致资金泄漏,并可能使您窒息)。正如Gaurav所提到的,在稳健地实现这种QCW模式时,还有一些其他考虑因素(如果我的节点失败,或者如果我的代码中有坏消息("毒")、错误等,会发生什么),但您的用例似乎并不太关心这一点,因为cron作业的下一次启动显然会导致基础设施中的任何(罕见但可能的)故障,并且可能假设没有致命的错误(这样您就不会被有毒消息卡住),等等。

将在队列中放置项目与处理队列中的项目解耦实际上是一个逻辑设计点。我的意思是,您可以随时更改这一点,并非常容易地将处理端(从队列中提取代码)移动到另一个应用程序层(服务层),而不会破坏基本设计的任何部分。这提供了很大的灵活性。大多数时候(两层),您甚至可以在一个Web角色节点上运行所有内容(如果您需要SLA,则可以在两个节点上运行,根据您的一些评论不确定您是否这样做),然后根据需要添加一堆处理虚拟机来实现三层,例如在新年期间。

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

由于我对你的应用程序了解不多,现在猜测性更强了

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

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

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

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

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

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

这有道理吗?

我肯定会选择一个横向扩展的解决方案:不那么复杂,更易于管理,定价更好。此外,在部署失败的情况下,您的停机风险较小(当然,故障和升级域的机制应该涵盖这一点,但无论如何)。因此,在这件事上,我完全支持高拉夫!

最新更新