azure - 水平扩展写入时如何避免并发问题?

标签 azure scalability sharding microservices horizontal-scaling

假设有一个工作服务从队列接收消息,从文档数据库中读取指定 ID 的产品,根据消息应用一些操作逻辑,最后将更新的产品写回数据库 (a) .

horizontally scaling writes

在处理不同的产品时,这项工作可以安全地并行完成,因此我们可以水平扩展 (b)。但是,如果多个服务实例在同一个产品上工作,我们可能会遇到并发问题,或者数据库并发异常,在这种情况下我们应该应用一些重试逻辑(并且重试仍然可能会再次失败等等) 。

问题:我们如何避免这种情况?有没有办法确保两个实例不在同一产品上运行?

示例/用例:一家在线商店对产品 A、产品 B 和产品 C 进行了大促销,一小时后结束,有数百名客户在购买。对于每次购买,都会排队一条消息(productId、numberOfItems、price)。 目标:我们如何运行工作人员服务的三个实例,并确保productA的所有消息最终都会到达instanceA、productB到instanceB以及productC到instanceC(从而不会出现并发问题)?

注释:我的服务是用 C# 编写的,作为辅助角色托管在 Azure 上,我使用 Azure 队列进行消息传递,并且我正在考虑使用 Mongo 进行存储。此外,实体 ID 是 GUID

更多的是关于技术/设计,所以如果你使用不同的工具来解决问题,我仍然感兴趣。

最佳答案

任何试图将负载分配到同一集合中不同项目(如订单)的解决方案都注定会失败。原因是,如果您的交易量很高,您就必须开始执行以下操作之一:

  1. 让节点互相交谈(嘿伙计们,有人在研究这个吗?)
  2. 将 ID 生成分成多个段(节点 A 创建 ID 1-1000,节点 B 1001-1999)等,然后让它们处理自己的段
  3. 将集合动态划分为段(并让每个节点处理一个段。

那么这些方法有什么问题呢?

第一种方法是简单地复制数据库中的事务。除非您可以花费大量时间来优化策略,否则最好依靠交易。

后两个选项会降低性能,因为您必须根据 id 动态路由消息,并且还要在运行时更改策略以包含新插入的消息。最终还是会失败。

解决方案

这里有两种解决方案,您也可以组合使用。

自动重试

相反,您在某处有一个从消息队列读取的入口点。

其中有这样的内容:

while (true)
{
    var message = queue.Read();
    Process(message);
}

为了获得非常简单的容错能力,您可以做的是在失败时重试:

while (true)
{
    for (i = 0; i < 3; i++)
    {
       try
       {
            var message = queue.Read();
            Process(message);
            break; //exit for loop
       }
       catch (Exception ex)
       {
           //log
           //no throw = for loop runs the next attempt
       }
    }
}

您当然可以捕获数据库异常(或者更确切地说事务失败)来重放这些消息。

微服务

我知道,微服务是一个流行词。但在这种情况下,这是一个很好的解决方案。不要使用处理所有消息的整体核心,而是将应用程序划分为更小的部分。或者在您的情况下,只需停用某些类型消息的处理。

如果您有五个节点运行您的应用程序,您可以确保节点 A 接收与订单相关的消息,节点 B 接收与运输等相关的消息。

通过这样做,您仍然可以水平扩展您的应用程序,不会发生冲突,并且几乎不需要付出什么努力(更多的消息队列并重新配置每个节点)。

关于azure - 水平扩展写入时如何避免并发问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28930710/

相关文章:

performance - MongoDB 分片集群比独立节点慢 25

python - 使用 Python 水平扩展或分片 Python-RQ 或 Redis

c# - DeviceClient 发生超时,但消息已发送到 IotHub

azure - Azure 是否可以通过 REST API 从 VM 获取文件?

python - 在 Python 中一次检查大量 IMAP 帐户

scalability - 将后台进程(celery)添加到 OpenShift 上的缩放应用程序

mongodb - mongodb 中的自定义范围分片键

azure - '在类型 'type' 的对象上找不到成员 'PolicyParameter'

angular - 无法读取未定义测试的属性 'MobileServiceClient'

sql - 数据库可扩展性 : Which is more important, 表的大小或查询的数量?