关于消息队列的一些思考

Posted radish40

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于消息队列的一些思考相关的知识,希望对你有一定的参考价值。

  1. 日志与消费队列
  2. 消息队列的应用价值
    • 数据集成于系统解耦
    • 异步处理与事件驱动
    • 流量削峰
    • 事务消息与分布式事务的最终一致
  3. 从历史看消息队列的价值演化

思考手上的工作,找到他的价值和定位,将价值最大化

1. 日志和消息队列

推荐文章:The Log: What every software engineer should know about real-time data\'s unifying abstraction | LinkedIn Engineering

日志的应用目标:它记录了什么时间发生了什么事情。

由于日志【按时间天生有序】的特性,解决了分布式系统中的两个重要问题:修改操作的顺序数据分发,这为并发更新的一致性和副本复制提供了基础。将协商出一致的修改操作顺序写入日志,,利用日志做修改操作的“数据分发”,使得各副本能够在本地应用完全相同的操作顺序,无需考虑网络延迟等其他因素对操作顺序的影响,从而保证各副本数据的一致。

本质上是把多台机器一起执行同一件事情的问题简化为实现分布式一致性日志,通过日志的 Pub/Sub 保证多题啊机器对数据处理的最终一致。

Primary 将各种操作通过日志序列化,各 Replica 从日志中读取并应用到本地状态,这种解决问题的模式也叫 Pub/Sub,即抽象成通用的数据订阅机制,而将这种抽象产品化,这就是 消息队列

2. 消息队列的应用价值

消息队列作为大型分布式系统的关键组件,在实时数据或流式数据架构中扮演这重要的角色。它通常被应用再系统解耦、异步处理、流量削峰,分布式事务/金融电商等场景中。

1. 数据集成&系统解耦

数据集成:将一个组织所拥有的数据,使其在所有的服务和系统中都可用。

数据集成的难点在于需要面对:

  • 越来越多的数据
  • 越来越多的数据系统

引入消息队列做数据分发。

好处:

  • 这样架构只需要关心如何将数据导入消息队列即可
  • 由于消息队列的解耦,架构复杂度大大降低,各系统间的数据同步不会互相影响
  • 由于消息队列的持久化,不易丢失数据,也可以在必要时进行数据重放
  • 由于所有数据都汇聚到消息队列中,数据集成很高效,可以快速介入新的系统而不影响存量架构

解耦是消息队列解决的最本质的问题,价值所在。

2. 异步处理&事件驱动

异步处理:本质上是解耦系统间的 RPC 调用,将那些依赖其他系统,同时对时效性要求不高的逻辑剥离出来,提高系统吞吐量和响应时间,降低系统耦合的复杂度。异步处理注重的是“通知”,事件驱动。

好处:

  • 简化业务逻辑,更快的返回结果,提升响应效率和系统吞吐,改善用户体验
  • 及时异步逻辑出现异常,也不会影响主流程,降低系统耦合度

异步处理更多的是事件驱动,引入消息队列做事件的转述与投递,从架构上也会达到解耦的功效。事件驱动的核心就是一套稳定、靠谱的 Event-Streaming 系统:消息队列。

大型分布式架构的数据总线、微服务中隔离直连的异步调用,事件驱动,处处都有消息中间件的身影。

3. 流量削峰

为什么要削峰:

  1. 后端服务的处理能力往往是有限的,无法和前端对等,同样的机器能够支撑的并发量无法跟前端处在同一个数量级。
  2. 后端服务对到达自身的流量是不可控的,只要上游调用不限流,后端就随时暴露在流量洪峰的压力下。

消息队列的作用:

  • 高吞吐的特点可以在不高的成本下承接住巨大的流量,起到蓄水池的作用
  • 后端采用拉取的消费模式,可以按照自身的处理能力进行流控,平稳的消费队列

4. 事务消息于分布式事务的最终一致

消息队列中的事务:指“业务执行本地事务”与“消息发送到消息队列“的原子性。

事务消息和普通消息的区别是,在事务提交前,这个消息对消费者来说是不可见的,基于事务消息,消息队列最终解决的是生产者与消费者在分布式事务场景中的一致性问题。

消费端对数据的处理逻辑一般是幂等的。

对于以上场景,引入消费队列做异步处理,使用非事务消息可能会出现如下情况:

  • 创建订单成功,写入消息失败导致使用的优惠券没有被删掉
  • 订单因支付失败没有创建成功,优惠券却被删除了

事务消息要解决的就是上述问题,保证“业务执行本地事务”(创建订单)和“发送消息”这两件事情是原子性的,然后依赖消息队列的可靠投递,实现不同系统对数据处理的最终一致性。

服务端为了实现事务机制(保障下游系统能消费到客户端认为已经提交的事务信息),需要做以下事情:

  1. 引入事务状态来“记录”客户端事务是否提交
  2. 由于网络原因或订单系统重启,导致有些客户端已提交或撤回的消息在服务端的状态是未知的。需要辅助“补偿"机制(对不确定状态的消息进行回查或客户端询问)来解决

通过两阶段提交语义(事务消息),事务状态反查的机制补偿,消息队列的可靠投递,以及业务消费逻辑的幂等,实现分布式事务最终一致。

3. 从历史看消息队列的价值演化

  1. 最早的消息队列开源产品 ActiveMQ,可以追溯到2003年,这一阶段主要是用来减轻经典 RPC 调用的紧耦合,剥离异步处理逻辑,解耦系统复杂度。(调用松耦合,异步处理
  2. 第二阶段便是以kafka为经典代表的实时数据管道时代,这一阶段的产品在吞吐和数据量上都有质的提升,主要是为了解决数据集成的痛点,提高数据在不同系统之间的流转效率。(数据管道,数据集成,实时计算
  3. 消息系统的第三个阶段,流数据平台,不仅仅是一个数据管道,更多的是强调平台化。平台化使得消息系统必须配备完善的多租户管理,I/O阻隔,流控与配额管理等。随着容器化发展,消息队列需要一个灵活的架构来适应这种变革,未来存储计算分离很可能是中间件产品的架构发展方向。(平台化,容器化,存储计算分离架构

关于消息队列的思考

做积极的人,而不是积极废人!

源码精品专栏

 

来源:http://t.cn/EbxTHT5

  • 消息队列组成

  • 消息队列的消费语义

    • 1、消息至多被消费一次

    • 2、消息至少被消费一次

    • 3、消息仅被消费一次

  • 结语


消息队列是服务架构中常见的组件,可用于服务间解耦、事件广播、任务异步/延迟处理等,本文对于消息队列的实现如何满足几种消费语义进行了阐述。

消息队列组成

  • 生产者(Producer):负责产生消息

  • 消息代理(Message Broker):负责存储/转发消息(转发分为推和拉两种,拉是指Consumer主动从Message Broker获取消息,推是指Message Broker主动将Consumer感兴趣的消息推送给Consumer)

  • 消费者(Consumer):负责消费消息

img

消息队列的消费语义

  1. 消息至多被消费一次

  2. 消息至少被消费一次

  3. 消息仅被消费一次

为了支持上面3种消费语义,可以分3个阶段考虑消息队列系统中Producer、Message Broker、Consumer需要满足的条件:

1、消息至多被消费一次

该语义是最容易满足的,特点是整个消息队列吞吐量大,实现简单。适合能容忍丢消息,消息重复消费的任务。

  • Producer发送消息到Message Broker阶段:Producer发消息给Message Broker,不要求Message Broker对接收到的消息响应确认,Producer也不用关心Message Broker是否收到消息了。

  • Message Broker存储/转发阶段:对Message Broker的存储不要求持久性,转发消息时也不用关心Consumer是否真的收到了。

  • Consumer消费阶段:Consumer从Message Broker中获取到消息后,可以从Message Broker删除消息,或Message Broker在消息被Consumer拿去消费时删除消息,不用关心Consumer最后对消息的消费情况如何。

2、消息至少被消费一次

适合不能容忍丢消息,允许重复消费的任务。

  • Producer发送消息到Message Broker阶段:Producer发消息给Message Broker,Message Broker必须响应对消息的确认。

  • Message Broker存储/转发阶段:Message Broker必须提供持久性保障,转发消息时,Message Broker需要Consumer通知删除消息,才能将消息删除。

  • Consumer消费阶段:Consumer从Message Broker中获取到消息,必须在消费完成后,Message Broker上的消息才能被删除。

3、消息仅被消费一次

适合对消息消费情况要求非常高的任务,实现较为复杂。

在这里需要考虑一个问题,就是这里的“仅被消费一次”指的是如下哪种场景:

  • Message Broker上存储的消息被Consumer仅消费一次

  • Producer上产生的消息被Consumer仅消费一次

Message Broker上存储的消息被Consumer仅消费一次 场景要求:

  • Producer发送消息到Message Broker阶段:Producer发消息给Message Broker,不要求Message Broker对接收到的消息响应确认,Producer也不用关心Message Broker是否收到消息了。

  • Message Broker存储/转发阶段:Message Broker必须提供持久性保障,并且每条消息在其消费队列里有唯一标识(这个唯一标识可以由Producer产生,也可以由Message Broker产生)。

  • Consumer消费阶段:Consumer从Message Broker中获取到消息后,需要记录下消费的消息标识,以便在后续消费中防止对某个消息重复消费(比如Consumer获取到消息,消费完后,还没来得及从Message Broker删除消息,就挂了,这样Message Broker如果把消息重新加入待消费队列的话,那么这条消息就会被重复消费了)。

Producer上产生的消息被Consumer仅消费一次 场景要求:

  • Producer发送消息到Message Broker阶段:Producer发消息给Message Broker,Message Broker必须响应对消息的确认,并且Producer负责为该消息产生唯一标识,以防止Consumer重复消费(因为Producer发消息给Message Broker后,由于网络问题没收到Message Broker的响应,可能会重发消息给到Message Broker)。

  • Message Broker存储/转发阶段:Message Broker必须提供持久性保障,并且每条消息在其消费队列里有唯一标识(这个唯一标识需要由Producer产生)。

  • Consumer消费阶段:Consumer从Message Broker中获取到消息后,需要记录下消费的消息标识,以便在后续消费中防止对某个消息重复消费(比如Consumer获取到消息,消费完后,还没来得及从Message Broker删除消息,就挂了,这样Message Broker如果把消息重新加入待消费队列的话,那么这条消息就会被重复消费了)。

结语

现在业内已经有许多成熟的消息队列的实现了,对于选择用哪一个实现,可以先根据业务需要支持的消费语义进行初步筛选,之后再根据运维难度、社区活跃度、性能、可用性等综合考虑选择合适的消息队列系统,如何判断一个消息队列实现是否支持某个消费语义,根据本文中阐述的3个阶段去判断即可。





关于消息队列的思考

已在知识星球更新源码解析如下:

关于消息队列的思考


人若无名↓↓↓↓↓

专心练剑↓↓↓



以上是关于关于消息队列的一些思考的主要内容,如果未能解决你的问题,请参考以下文章

系统引入消息队列的思考

关于消息队列的理解

关于mq的思考

关于消息队列,你必须知道的一些事

消息队列价值思考

redis源码学习redis 中的“消息队列” Stream