首页 文章

Windows Azure暂存< - >生成导致表存储上的冲突和错误

提问于
浏览
0

昨天我们尝试交换我们的暂存< - > 生产环境 角色时遇到了一个可怕的问题/经验 .

Here is our setup:

我们有一个workerrole从队列中获取消息 . 这些消息在角色上处理 . (表存储插入,db选择等) . 每个队列消息可能需要1-3秒,具体取决于他需要制作多少个表存储帖子 . 一切都完成后,他将删除该消息 .

Problem when swapping:

当我们的暂存项目上线时,我们的 生产环境 Worker 开始出错了 .

当角色想要处理队列消息时,它会不断地删除“ EntityAlreadyExists _958156” . 这导致队列消息被放回队列并返回处理等等....

在查看这些队列消息并分析它们会发生什么时,我们看到它们实际上已处理但未删除 .

删除这些错误消息时问题仍然没有结束 . 新的队列消息也没有被处理,而这些消息尚未处理,并且没有添加表存储记录,这听起来很奇怪 .

删除暂存和产品并再次发布到 生产环境 时,一切都开始正常工作 .

Possible problem(s)?

我们已经知道实际上发生了什么 .

  • 也许两个角色都拾取了相同的消息,一个发布了帖子,一个发生了错误?

  • ...... ???

Possible solution(s)?

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

  • 制作有害消息故障转移系统?当出队计数超过X时,我们应该删除该队列消息或将其放入单独的'poisonqueue'中 .

  • 捕获EntityAlreadyExists错误,只删除该队列消息或将其放在单独的队列中 .

  • ...... ????

Multiple roles

我想我们在担任多个角色时会遇到同样的问题?

非常感谢 .

EDIT 24/02/2012 - Extra information

  • 我们实际上使用 GetMessage()

  • 队列中的每个项目都是唯一的,并将在表存储中生成唯一的消息 . 关于流程的更多信息:用户发布内容并且必须分发给某些其他用户 . 从该用户生成的消息将具有唯一的Id(guid) . 此消息将发布到队列中,并由worker角色获取 . 消息分布在其他几个表中(partitionkey - > UserId,rowkey - >一些时间戳记的刻度和唯一的消息ID . 因此几乎没有机会在正常情况下发布相同的消息 .

  • 隐身超时 COULD be a logical explanation 因为某些消息可以分发到10-20个表 . 这意味着10-20插入没有批处理选项 . 你可以设置或扩展这个 invisibility time 吗?

  • 由于异常而不删除队列消息 COULD 也是一个解释因为我们没有实现任何有害消息故障超过YET;) .

5 回答

  • 1

    你在处理双重消息时显然有问题 . 您的ID是唯一的这一事实并不意味着在某些情况下消息不会被处理两次,例如:

    • 角色死亡并且部分完成工作,因此消息将重新出现以便在队列中进行处理

    • 角色崩溃意外,因此消息最终返回队列中

    • FC迁移移动您的角色而您没有代码来处理这种情况,因此消息最终返回队列

    在所有情况下,您都需要能够处理消息重新出现的代码 . 一种方法是使用DequeueCount属性并检查消息从队列中删除并接收进行处理的次数 . 确保您拥有处理消息部分处理的代码 .

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

  • 1

    无论分段与 生产环境 问题如何,拥有处理有害消息的机制至关重要 . 我们在Azure队列上实现了一个抽象层,一旦尝试处理一些可配置的次数,就会自动将消息移动到毒性队列 .

  • 1

    有几个可能的原因:

    你是如何阅读队列消息的?如果您正在执行Peek消息,则在删除消息之前,该消息仍然可见,以便由另一个角色实例(或您的暂存环境)获取 . 你想要确保您使用的是“获取消息”,因此消息在被删除之前是不可见的 .

    在完成消息工作之后但是在删除消息之前,您的第一个角色是否可能崩溃了?这将导致消息再次可见并被另一个角色实例接收 . 此时,该消息将是一个有害消息,将导致您的实例不断崩溃 .

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

    一般来说,你需要处理有害消息,所以你需要实现这个逻辑,但我建议首先找到这个问题的根本原因,否则你将在以后遇到更多的问题 .

  • 1

    对于队列,您需要考虑到幂等性并期望并将“EntityAlreadyExists”作为可行的响应处理 .

    正如其他人所说,原因可能是

    • 队列中具有相同标识符的多条消息 .

    • 正在查看邮件而不是从队列中读取它,因此不会使它们不可见 .

    • 不删除邮件,因为在删除邮件之前抛出了异常 .

    • 处理邮件的时间太长,因此无法删除(因为隐身已超时)并再次出现

    我没有看代码,我猜它正在发生的是3或4选项 .

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

    在多角色环境中有效地使用队列需要稍微不同的心态,并且尽早遇到这些问题实际上是伪装的祝福 .

    Appended 2/24

    只是为了澄清,修改隐形时间不是这类问题的通用解决方案 . 另请注意,虽然REST API上提供此功能,但在队列客户端上可能无法使用 .

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

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

    如果你正在进行更新,那么它就是一个不同的球类游戏 . 一种模式是将更新与具有相同分区键的同一表中的虚拟插入配对,以便在先前发生更新时发生错误,因此跳过更新 . 删除消息后,您可以删除虚拟插入 . 然而,所有这些都增加了复杂性,因此重新审视产品的架构要好得多;例如,你真的需要插入/更新这么多表吗?

  • 2

    在不知道你的工作者角色实际在做什么的情况下,我在这里猜测,但听起来好像你有两个你的工作者角色运行实例,你在尝试写入Azure表时遇到了冲突 . 这可能是因为您的代码看起来像这样:

    var queueMessage = GetNextMessageFromQueue();    
    
    Foo myFoo = GetFooFromTableStorage(queueMessage.FooId);
    
    if (myFoo == null)
    {
        myFoo = new Foo {
                            PartitionKey = queueMessage.FooId
                        };
    
        AddFooToTableStorage(myFoo);
    }
    
    DeleteMessageFromQueue(queueMessage);
    

    如果队列中有两个相邻的消息具有相同的 FooId ,那么很可能你最终会检查两个实例是否存在 Foo ,而不是找到它然后尝试创建它 . 无论哪个实例是最后一个尝试并保存该项目将得到"Entity already exists"错误 . 因为它的错误,它永远不会到达代码的删除消息部分,因此在一段时间后它会在队列中变得可见 .

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

    Update 27/02 如果它's not subsequent messages (which based on your partition/row key scheme I would say it'不大可能),那么我的下一个赌注是's the same message appearing back in the queue after the visibility timeout. By default if you'重新使用.GetMessage()超时为30秒 . 它有一个overload,允许您指定该时间范围的长度 . 还有.UpdateMessage() function允许您更新该超时,因为您在50秒后仍然处理该消息,将其延长一分钟 .

相关问题