我们昨天尝试交换我们的升级时遇到了一个可怕的问题/经历< - >生产角色。
以下是我们的设置:
我们有一个workerrole从队列中获取消息。这些消息在角色上处理。 (表存储插入,db选择等)。每个队列消息可能需要1-3秒,具体取决于他需要制作多少个表存储帖子。一切都完成后,他将删除该消息。
交换时出现问题:
当我们的暂存项目上线时,我们的生产工人开始出错了。
当角色想要处理队列消息时,它会给出“ EntityAlreadyExists ”错误的持续流。由于这些错误,队列消息未被删除。这导致队列消息被放回队列并返回处理等等......
当查看这些队列消息并分析它们会发生什么时,我们看到它们实际上已被处理但未被删除。
删除这些错误消息时问题仍然没有结束。新的队列消息也没有被处理,而这些消息尚未处理,也没有添加任何表存储记录,这听起来很奇怪。
当删除暂存和产品并再次发布到生产时,一切都开始正常工作。
可能出现的问题?
我们已经知道实际上发生了什么。
可能的解决方案?
我们对如何解决这个“问题”有一些想法。
多个角色
我想我们在设置多个角色时会遇到同样的问题?
非常感谢。
编辑24/02/2012 - 额外信息
答案 0 :(得分:2)
无论分段与生产问题如何,拥有处理有害消息的机制至关重要。我们在Azure队列上实现了一个抽象层,一旦尝试处理一些可配置的次数,就会自动将消息移到毒性队列中。
答案 1 :(得分:1)
有几个可能的原因:
您是如何阅读队列消息的?如果您正在执行Peek消息,则在删除消息之前,该消息仍然可供另一个角色实例(或您的暂存环境)使用。您希望确保使用“获取消息”,以便消息在被删除之前不可见。
在完成邮件工作之后但是在删除邮件之前,您的第一个角色是否可能崩溃了?这将导致消息再次可见并被另一个角色实例接收。此时,该消息将成为有害消息,这将导致您的实例不断崩溃。
这个问题几乎肯定与Staging vs Production无关,但很可能是因为有多个实例从同一队列中读取而引起的。您可以通过指定2个实例,或者通过将相同的代码部署到2个不同的生产服务,或者使用2个实例在您的开发机器上本地运行代码(仍然指向Azure存储)来重现相同的问题。
一般情况下,您需要处理有害消息,因此无论如何都需要实现该逻辑,但我建议首先找到此问题的根本原因,否则您将在以后遇到更多问题。
答案 2 :(得分:1)
在不知道你的工作者角色实际在做什么的情况下,我在这里猜测,但听起来好像你有两个工作者角色运行实例,你在尝试写入Azure表时遇到了冲突。这很可能是因为你的代码看起来像这样:
var queueMessage = GetNextMessageFromQueue();
Foo myFoo = GetFooFromTableStorage(queueMessage.FooId);
if (myFoo == null)
{
myFoo = new Foo {
PartitionKey = queueMessage.FooId
};
AddFooToTableStorage(myFoo);
}
DeleteMessageFromQueue(queueMessage);
如果队列中有两条相邻的消息具有相同的FooId
,那么很可能您最终会检查两个实例是否存在Foo
,而不是找到它然后尝试创建它。无论哪个实例是最后一个尝试并保存该项目将获得“实体已存在”错误。因为它出错了所以它永远不会到达代码的删除消息部分,因此在一段时间后它会在队列中变得可见。
正如其他人所说,处理毒药信息是一个非常好的主意。
2002年2月27日更新 如果它不是后续消息(基于您的分区/行密钥方案,我会说它不太可能),那么我的下一个赌注是在可见性超时后出现在队列中的相同消息。默认情况下,如果您使用.GetMessage(),则超时为30秒。它有一个overload,允许您指定该时间范围的长度。还有.UpdateMessage() function允许您在处理消息时更新该超时。例如,您可以将初始可见性设置为1分钟,然后如果您仍在50秒后处理该消息,请将其延长一分钟。
答案 3 :(得分:1)
对于队列,您需要考虑到幂等性并期望并处理“EntityAlreadyExists”作为可行的响应。
正如其他人所说,原因可能是
不看代码,我猜它正在发生的是3或4选项。
如果您无法通过代码审查检测到问题,可以考虑添加基于时间的日志记录和try / catch包装器以便更好地理解。
在多角色环境中有效地使用队列需要稍微不同的心态,并且尽早遇到这些问题实际上是伪装的祝福。
附加2/24
只是澄清一下,修改隐形时间并不是解决此类问题的通用方法。另请注意,虽然REST API上提供此功能,但可能在队列客户端上不可用。
其他选项涉及以异步方式写入表存储以加快处理时间,但这又是一个停止间隙度量,它并没有真正解决使用队列的基本范例。
所以,底线是幂等的。您可以尝试使用表存储upsert(更新或插入)功能,以避免出现'EntitiyAlreadyExists'错误,如果这适用于您的代码。如果您所做的只是将新实体插入到azure表存储中,那么upsert应该可以通过最少的代码更改来解决您的问题。
如果你正在进行更新,那么它就是一个不同的球类游戏。一种模式是将更新与具有相同分区键的同一表中的虚拟插入配对,以便在先前发生更新时发生错误,因此跳过更新。删除消息后,您可以删除虚拟插入。然而,所有这些都增加了复杂性,因此重新审视产品的架构要好得多;例如,你真的需要插入/更新到这么多的表吗?
答案 4 :(得分:1)
您在处理双重讯息时显然有问题。您的ID是唯一的这一事实并不意味着在某些情况下消息不会被处理两次,如:
在所有情况下,您都需要能够处理消息重新出现的代码。一种方法是使用DequeueCount属性并检查消息从队列中删除并接收进行处理的次数。确保您拥有处理消息部分处理的代码。
现在在交换过程中可能发生的事情是,当生产环境成为升级和升级生产时,他们都试图接收相同的消息,因此他们基本上互相竞争这些消息,这可能并不坏,因为无论如何,这是一种已知的模式,但是当你杀死旧的生产(暂存)收到的每个处理消息但尚未完成的消息时,最终回到队列中,你的新生产环境又选择了消息进行处理。没有代码逻辑来处理这种情况,并且消息是部分处理的,表中的一些记录存在并且它开始引起您注意到的行为。