0

我有以下设置:部署了 N 个 Azure Worker Role 实例。我们的桌面应用程序将一条消息上传到 Azure,然后上传一组与该消息相关的图像。Message 知道它需要什么图像。

这 2 个活动(消息上传和图像上传)是独立的- 图像甚至可以在用户生成消息之前上传(称为缓存 - 但它更复杂),或者在消息上传到 Azure 之后几秒/分钟。

我将消息存储在 Azure MSSQL 数据库中,图像存储在 blob 中,它们的 URL 存储在数据库中。还有一个MessageToImage表,它存储指向消息图像的链接。这是一个简化的数据库结构(请原谅我的 C#):

class Message
{
    public int Id;
    public string Text;
}

class Image
{
    public int Id;
    public string Name;
    public string BlobUrl;   // Null if image was not received by the service yet
}

class MessageToImage
{
    public int MessageId;
    public List<int> ImageIds;
}

当我们准备好所有图像的消息(即所有图像都已上传)时,我们需要对其进行其他操作(例如,发布到 Facebook)。问题是:我如何保证该消息只会被处理一次?在最坏的情况下,我将有 N 个实例同时接收 N 个消息的图像 - 哪个实例将“选择”它应该将消息发送到进一步处理?我怎么能保证它只会发生一次?

到目前为止,我提出了以下想法:

  1. 确保“为图像更新 BlobUrl”数据库逻辑将是原子的,并将返回消息的“丢失”图像的数量。这样,我将仅在一个实例上触发进一步处理 - 作为数据库更新结果接收“0”的实例。但是:我怎样才能在 MSSQL 级别上做到这一点?更复杂 - 我如何使用实体框架来做到这一点?

  2. 有一个专门的工作人员角色,该工作将选择具有所有图像的消息 - 并将它们发送以进行处理。但这不能很好地扩展......而且看起来有点难看。

还有其他想法/建议吗?

谢谢!


UPDATE1 @Richard 和 @Rob 建议使用服务总线队列。我确实调查过了。我仍然没有答案的部分是 WORKER ROLE 中决定何时将消息发送到队列进行处理的代码应该是什么样子?仅当数据库/blob 中存在所有图像(即上传到 Azure 云)时,才会将消息发送到队列。在这里,我仍然想指出我的极端案例——我有 10 个图像同时由 10 个工作角色处理。对于所有实例,处理同时结束。每个角色都使用上传的图像 URL 更新数据库。然后我应该以某种方式触发最终的消息处理——这意味着其中一个实例应该获得优先级。而且我不清楚我应该如何做到这一点。

希望这使我的问题更清晰。

4

2 回答 2

2

创建一个 Azure 服务总线队列,并让您​​的客户端应用程序将消息发布到队列。然后,您的工作角色可以从队列中提取消息并处理消息。

服务总线队列的伟大之处在于它们保证消息只能从队列中拉出一次,然后将消息标记为“已获取”。如果事务未在(可配置的)时间段内标记为完成,则消息将返回到队列,准备好被下一个工作请求拉取。

这意味着,如果您的工作人员角色在处理过程中失败,消息最终将重新出现在队列中,让下一个工作人员来接它并(希望)完成所需的工作。

阅读本文了解更多信息:

如何使用服务总线队列

于 2013-05-22T23:47:12.867 回答
1

您应该考虑使用服务总线消息传递会话

在您的客户端上为上传会话生成一个唯一的批量上传 ID(使用 GUID 而不是 int)。上传的每张图片都应附有此 ID(上传批次中所有图片的 ID 相同)。当您的服务收到图像时,它会将“上传图像” BrokeredMessage 发布到服务总线队列或主题,并将 SessionId 设置为客户端提供的唯一 Id。

当您的消息从客户端发送到服务时,发送批量上传 ID。当从服务收到此消息时,会在另一个队列/主题上发布“处理所有上传”BrokedMessage。这个 BrokeredMessage 的接收者将读取批量上传 Id,然后开始侦听与相关 sessionId 关联的所有“上传图像”消息。它可以继续执行此操作,直到全部收到(可能需要发送上传的总计数,以便知道何时停止)。一旦它收到它们,它就可以生成一个新的 BrokeredMessage 来触发图像上传的处理(例如发布到 facebook)。

您想为最终处理阶段(发布到 facebook)生成一个单独的 BrokeredMessage,这样如果您的角色在处理消息时失败,消息将在稍后重新传递。

需要记住的是,“至少一次”接收到消息,如果您的角色在处理消息中途失败,则消息将在超时后返回到队列/主题。因此,您的处理逻辑需要能够处理它正在处理之前已经部分处理的消息的情况。

以下是各个参与者为实现您想要的目标而应该做的大致的事情:

客户

  • 生成一个唯一的 sessionId(或从您的 Web 服务请求一个)
  • 将每个图像上传到 Web 角色,并带有关联的 sessionId
  • 将作业“消息”上传到网络角色,并在上传批次中关联 sessionId 和 imageCount(可以在图像上传之前/期间/之后完成)

网络角色

  • 当接收到上传的图像时,将图像存储在 blob 存储中,向队列(“UploadsQueue”)发布带有 SessionId 设置和包含图像 URL 的正文的 BrokeredMessage
  • 接收上传的作业“消息”时,向队列 (“JobsQueue”) 发布一个“UploadBatch” BrokeredMessage,其正文包含 sessionID 和 imageCount

工人角色

  • 当从“JobsQueue”接收到“UploadBatch”消息时,从消息正文中获取 sessionId 和 imageCount,在“UploadsQueue”上获取 AcceptMessageSession(sessionId),并继续轮询该会话中的消息,直到收到所有上传消息。一次(receivedCount == imageCount)向“JobsQueue”发布“SendToFacebook”消息,并在正文中包含要处理的图像列表等
  • 从“JobsQueue”接收“SendToFacebook”消息时,从消息正文中获取要处理的图像列表并将它们发送到 facebook 等
于 2013-05-23T00:13:47.037 回答