2012-02-23 51 views
0

昨天当我们尝试更换我们的分段< - >生产角色时,我们遇到了可怕的问题/体验。Windows Azure升级<-->生产导致表存储中的冲突和错误

这里是我们的设置:

我们有一个workerrole从队列中拿起消息。这些消息在角色上处理。 (表存储插入,数据库选择等)。这可能需要每队列消息1-3秒,具体取决于他需要创建多少表格存储帖子。一切完成后,他将删除该消息。

问题交换时:

当我们的分期项目上线我们的生产workerrole开始示数。

当角色想要处理队列消息时,它给了'EntityAlreadyExists'错误的持续流。由于这些错误,队列消息未被删除。这导致队列消息被放回到队列中并返回到处理等。......

当查看这些队列消息并分析将发生什么事情时,我们看到它们实际已处理但未被删除。

删除这些错误消息时,问题还没有结束。新的队列消息没有得到处理,而这些消息尚未处理,也没有添加表存储记录,这听起来很奇怪。

当删除分段和生产并再次发布到生产时,一切都开始正常工作。

可能的问题?

我们有litle 2不知道实际发生了什么事。

  • 也许这两个角色拿起相同的消息,一个发布了帖子,一个发生了错误?
  • ... ???

可能的解决方案?

我们对如何解决这个“问题”有一些想法。

  • 让毒讯息在系统中失效?当出队计数超过X时,我们应该删除该队列消息或将其放入单独的“poisonqueue”中。
  • 捕获EntityAlreadyExists错误,只删除该队列消息或将其放入单独的队列中。
  • ... ????

的多重角色

我想我们都会有同样的问题搭建多个角色时?

非常感谢。

编辑24/02/2012 - 额外的信息

  • 我们实际使用的的GetMessage()
  • 在队列中的每个项目都是独特的,将产生表存储唯一的消息。关于该过程的更多信息:用户发布内容并且必须分发给某些其他用户。从该用户生成的消息将具有唯一的Id(guid)。该消息将被发布到队列中并由工作者角色提取。该消息分布在多个其他表(分区键 - > UserId,rowkey - >某些时间戳记&中唯一的消息ID。因此几乎没有机会在相同的消息发布在正常情况下
  • 不可见时间出可能是一个合乎逻辑的解释,因为有些消息可能被分配到喜欢10-20表。这意味着10-20插入不批选项。你可以设置或扩大这一可见时间出来?
  • 不删除队列消息,因为有异常可能也是一个解释,因为我们没有实现任何有毒消息故障转移YET;)。
+0

最新的队列实现允许您更改可见性超时。 http://msdn.microsoft.com/en-us/library/windowsazure/hh452234.aspx – hocho 2012-02-24 16:55:52

回答

1

你在处理双重信息时显然有错。你的ID是唯一的,但这并不意味着该消息不会像一些场合两次处理:

  1. 角色死亡,并与部分完成的工作,因此,该消息将在处理重新出现队列
  2. 作用崩溃意外,因此消息放回队列
  3. 的FC移植移动你的角色结束了,你不用代码来处理这种情况,所以在消息队列中结束了回来

在所有情况下,您都需要处理以下事实的代码:该消息将重新出现。一种方法是使用DequeueCount属性,并检查消息从队列中删除并接收处理的次数。确保你有处理消息部分处理的代码。

现在,交换过程中可能发生的情况是,当生产环境变为暂存并且暂存生产时,它们都试图接收相同的消息,因此它们基本上是相互竞争的,这可能并不坏因为这是一种已知的模式,但无论如何,除非您杀死旧生产(暂存)接收到的用于处理且未完成的每条消息,最终返回到Queue中,并且您的新生产环境再次选取要处理的消息。没有代码逻辑来处理这种情况,并且一条消息被部分处理,表中的一些记录就存在了,它开始引起你注意到的行为。

2

无论分阶段还是生产问题,使用处理有毒消息的机制都至关重要。我们已经在Azure队列上实现了一个抽象层,一旦尝试处理一些可配置的时间量,消息就会自动将消息转移到毒性队列中。

1

有几种可能的原因:

你如何阅读队列消息?如果您正在执行Peek消息,那么在消息被删除之前,消息仍然可以被另一个角色实例(或您的分段环境)所看到。您希望确保您使用“获取消息”,这样消息才可以被删除。

在删除邮件之前,您的第一个角色是否可能在完成邮件工作之后崩溃?这将导致消息再次变为可见,并被另一个角色实例拾取。此时该消息将成为一个毒害消息,这将导致您的实例不断崩溃。

这个问题几乎肯定与Staging vs Production无关,但很可能是由于有多个实例从同一个队列中读取而导致的。通过指定2个实例,或者通过将相同的代码部署到2个不同的生产服务,或者通过使用2个实例在您的开发机器上本地运行代码(仍指向Azure存储),您可能会再现相同的问题。

一般来说,你确实需要处理中毒消息,所以你需要实现这个逻辑,但我会建议先解决这个问题的根本原因,否则你将在稍后遇到更多问题。

+0

感谢您的评论。我已编辑我的帖子,更多的信息:) – 2012-02-24 07:58:20

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是否存在,但没有找到它然后尝试创建它。无论哪个实例是最后一次尝试并保存该项目都会得到“实体已存在”错误。因为它发生错误,所以它永远不会到达代码的删除消息部分,因此它会在一段时间后重新回到队列中。

正如其他人所说,处理毒讯息是一个非常好的主意。

更新27/02 如果不是后续消息(这基于你的分区/行密钥方式我会说这是不可能的),那么我的下一个赌注将是它的后在队列中出现的背面,同样的消息能见度超时。默认情况下,如果您使用.GetMessage(),则超时时间为30秒。它有一个overload,它允许您指定该时间范围的时间长度。还有.UpdateMessage() function可让您在处理邮件时更新该超时。例如,您可以将初始可见性设置为1分钟,如果您在50秒后仍在处理该消息,请再延长一分钟。

+0

这些消息是100%独特的:)我更新了一些信息。 – 2012-02-24 07:59:28

1

对于需要考虑幂等性的队列,期望并处理'EntityAlreadyExists'作为可行的响应。

正如其他人建议的,原因可能是队列中的具有相同标识符

  • 多个消息。
  • 正在偷看邮件,而不是从队列中读取它,因此不会使它们看不见。
  • 不删除邮件,因为在删除邮件之前抛出异常。
  • 花太长时间处理消息,因此它不能被删除(因为隐形已超时),并再次

出现不看我猜测,这是无论是3或4个选项是代码发生。

如果您无法通过代码审查发现问题,可以考虑添加基于时间的日志记录和try/catch包装以更好地理解。

在多角色环境中有效地使用队列需要稍微不同的思维方式,早期遇到这样的问题实际上是变相的祝福。

追加2/24

只是为了澄清,修改不可见时间不出来是一个通用的解决方案,这种类型的问题。另请注意,尽管REST API中提供了此功能,但在队列客户端上可能不可用。

其他选项涉及以异步方式写入表存储以加快处理时间,但这又是一个停止间隔度量,它并没有真正解决使用队列的基本范式。

所以,底线是幂等的。您可以尝试使用表存储插件(更新或插入)功能,以避免出现'EntitiyAlreadyExists'错误,如果这适用于您的代码。如果您所做的只是将新实体插入到azure表存储中,那么upsert应该用最少的代码更改来解决您的问题。

如果你正在做更新,那么它是一个不同的球类比赛。一种模式是将更新与同一表中具有相同分区键的伪插入进行配对,以便在更新之前发生错误并因此跳过更新。稍后消息被删除后,您可以删除虚拟插入。但是,所有这些都增加了复杂性,所以重新审视产品的架构会更好。例如,你是否真的需要插入/更新到如此多的表中?

+0

你的3和4选项'看起来'确实是对的。我发布了更多信息。 – 2012-02-24 07:58:53

相关问题