分布式系统消息幂等性保证的几点思考 -- 持续更新

概述

    分布式系统为了达到高可用、高性能和高可扩展性特性,拥有众多服务节点,服务调用链冗长复杂,服务节点之间网络通信复杂度指数级增加,一处轻微网络故障即可能导致整个服务异常。此外,考虑到硬件设备自身故障的可能性,所以分布式系统面临一个共同的问题:一个消息(任务)可能会被重复消费。如何确保同一个消息单次消费和多次重复消费具有相同的效果,也即消息的幂等性是一个热点问题。


架构图

  • 单体架构
    分布式系统消息幂等性保证的几点思考 -- 持续更新_第1张图片
  • 分布式架构
    分布式系统消息幂等性保证的几点思考 -- 持续更新_第2张图片

问题

  • 如何避免同一条消息被服务中不同实例重复消费
    • 场景一:同一条消息同时分配或者先后分配给多个实例
    • 场景二:一条消息先分配给实例1处理,处理失败后(业务流程自身错误,网络超时,实例1服务宕机),分配给实例2处理

解决方案:

  • 场景一:

  • 任务分配服务处理
        任务分配服务利用一个表来记录所有接收到并且成功被处理的消息。具体流程:任务分配服务收到API网关路由过来的消息后,依据既定分配策略,将该消息分配给当前服务的某一个实例处理,同时标记为为已分配状态。此后,若该消息被成功消费,则将该消息标记为已消费状态;若未被成功消费,则删除该记录。

  • 场景二:

  • 任务分配服务处理
        若实例无状态,则消息多次执行必定满足幂等(集中式存储可以利用其事务特性来保证)。反之,若实例有状态,此场景最优解是将同一个重复请求路由到特定实例来避免重复消费,但是此方案无法应对服务实例数量变更场景。此外,服务有状态情况下,需要考虑不同服务实例之间的数据同步。

  • 消费者处理
        此种场景下,任务分配服务仅仅负责通知所有可用服务实例有新消息到达,等待被消费。同一服务的多个实例竞争性消费该消息。此时需要借助于分布式锁机制来取得消息的消费权限。
        若获取消费权的服务无状态,则不同实例对某一请求的重复执行可以做到仅有一次成功。反之,若取得消费权限的服务有状态,则同上,需要考虑不同服务实例之间的数据同步问题。


总结

    无论哪种场景,分布式系统中为了避免消息被重复消费,我们可以考虑从任务(消息)分配端或者消费者两个角度来入手。
    如果数据库提供了读写分离功能,由于主从同步延迟,某一条消息在主库中已经存在被消费记录但是从库未能及时获取到该记录,则读从库时候会发现该消息未被消费,此时很容易出现重复消费问题。为了解决该问题,我们可以采用二次读策略,读从库未查询到特定记录后再读一次主库。

你可能感兴趣的:(后端开发,分布式,消息幂等,单体应用,消息重复消费)