消息中间件 零

消息中间件是什么?

MOM Message-Orientd Middleware is software infrastructure focused on sending and receiving messages between distributed systems.
消息中间件就是在分布式系统中发送和接收信息的软件.

从定义知, 我们通常说的消息中间件指的是在分布式系统中收发信息的工具. 这里涉及到两个关键词, 分布式系统和收发信息(线程间的消息通信工具不在本篇介绍范围). 也就是说, MOM是为了解决分布式系统中某些需求而应运而生的, 下面我们来分析下MOM的使用场景.

为什么使用MOM

想象下这样的场景, A系统执行pA操作, 该操作不依赖B系统, 但是B系统依赖pA, 需要知道pA每次的执行结果. 通常情况下, 我们可能会在pA执行之后远程调用B系统, 那么A系统本来不依赖B现在却要等待B系统响应, 这样显然是合理的. 那么优化下, 使用异步调用怎么样? 当然可以, 异步就不用等待B系统的响应. 但是还存一些问题: 虽然A系统不依赖B系统, 不用关心B系统执行结果, 但是B系统依赖A系统, 那么A系统是否成功调用B系统对B系统就非常重要, 那么谁来保证调用的成功性, 当然是A系统; 不, 张绣说我们用了非常NB的RPC框架, 疯狂重试, 重试到天荒地老, 保证可以调用B系统, 那你赢了Orz, 在A系统不挂的情况下. 在pA执行成功后, 在调用时出现问题是很可能出现的, 那就需要这个NB的RPC框架拥有存储信息的能力.
另一个问题, 现在C系统跟B系统同样的情况, 需要接收pA结果, 我们当然可以上述那样在pA执行后调用, 但是你看到后面排队的Z系统了么. 这难不倒刘秀, 刘秀有非常NB的代码生成工具, 通过配置文件引入不同的实现类, 然后异步循环执行每个实现类, 至于实现类中如何实现, 需要各个系统提供, 我们可以以引用jar包的方式来引入不同的实现类(我想象的, 感觉能实现.).
总结下, 两位秀儿是从不同方面想要解决A系统和依赖它的各个系统的耦合性问题, 张绣的思路还是比较正确的, 通过加一层处理逻辑, 剥离A系统和其他系统的关系, 那么这个NB的PRC框架, 拥有可以收发信息, 并且可以存储信息的工具就是MOM了. 而刘秀的处理只是在某些程度上减少了调用逻辑的代码, 从根本上没有解决系统之间的耦合性. 现在有了MOM, 刘秀就可以让MOM去执行他的各种实现类了.

MOM的基本能力

所以MOM为我们解决这样的问题:在一个分布式系统中, 多个系统依赖A系统, 但是A系统执行不依赖这些系统, 我们希望在不影响A系统执行的前提下, 将执行结果通知其他系统. 当依赖系统增加时, 不需要修改A系统的业务代码.
使用MOM解决了A系统和其他系统的耦合性, 转而与MOM耦合. 既然大家这么信赖MOM, MOM就需要用实际行动回报大家. MOM的基本功能就是接收生产者(Producer P, 比如A系统)的消息(pA处理结果)并把消息传递给消费者(Consumer C, 比如B系统).
** P -> M -> C**
在这个过程中, MOM需要保证: 接收到生产者信息后, 保证消息送达消费者, 并被消费者成功消费. 也就是说, 只要Producer业务执行成功, Consumer就必须业务执行成功(可以延迟 但是最终要执行成功).
在这里插入图片描述

在这个过程中, 可能会出现如图问题:
1 Producer执行业务成功后, 发送信息给MOM失败;
2 MOM成功接收Producer消息, 但是在发送给Consumer之前由于各种原因, MOM挂掉了
3 MOM发送给Consumer失败
4 MOM成功发送消息给Consumer, 但是Consumer执行业务失败
以上出现任何问题, 都会造成消息不一致. 在了解目前主流MOM之前, 我们先自己尝试下(找资料), 如何解决上述问题.

Producer成功发送信息到MOM

这个是在系统处理完业务之后, 发送信息后通知其他系统业务操作结果. 此时如果发送失败会导致生产者数据和消费者数据不一致.所以我们要保证业务和发送信息的一致性, 由于这些操作都在本地事务中, 所以我们只需要获取到发送信息后的结果, 就可以保证一致性, 所以MOM在接收到信息后需要发送一个确认收到的信息.

MOM在接收到信息后挂掉了

在MOM接收到信息后, 避免消息丢失, 当然要对消息持久化, 通过设置消息状态来区分哪些信息已经消费, 哪些还没有, 挂掉后通过状态继续发送还没有消费的信息.
这里有个问题, 尽管我们会对消息进行持久化, 但是也存在某个时刻, MOM接收到了信息, 还没有进行持久化就挂掉了, 那么这时候信息不就丢失了么? 后面我们看看主流MOM是如何解决这个问题的.

MOM发送给Consumer失败

MOM发送消息给Consumer, 但是一直没有收到Consumer确认收到的信息, 那MOM只需要一直重试就好了.

Consumer执行失败

与上面问题的区别是, 这次Consumer已经告诉MOM, 我已经收到信息了, 但是执行业务失败, 为了保证业务不丢失信息, 在执行前要先保存信息, 保存操作放在Consumer端显然不合适, 这样每个Consumer都需要自己实现一套保存逻辑, 所以保存信息还是有MOM来做. 像我们上面说的一样, 只需要设置消息状态为"未消费", 后面重试就可以该问题.

通信图

根据我们上述的方案, 修改通信图如下
在这里插入图片描述

MOM应用级功能

只拥有上述基本功能的MOM只是个玩具, 完全无法在实际中使用, 在实际使用中, 除了基本功能的实现, 高可用性是非常重要的. 而且在上面讲到的基础能力中, 我们多次使用重试机制处理异常情况, 那么这时候很容易出现重复消息的情况, 如何进行去除同样是需要关注的. 下面我们再想想如何设计.

集群

大部分的分布式集群结构都是由一个master node和多个普通node构成, master负责管理node, 不处理业务, node主要负责处理业务. 为了高可用, 可以设计secondary master防止master挂掉, 或者根据各种NB的选举算法从node中选出master.
集群的话, 需要考虑node挂掉, 或者扩容的情况. 这两种情况其实都是一个问题: 消息的重新分配, 无论是node挂掉还是增加新的node, 都需要将原来某个node下的信息重新分配给其他node. 所以为了解决上述问题, 我们的消息必须增加一个字段, 用来标记该消息是属于哪个node的, 然后根据某个分配算法(最简单的轮询)分发消息.

消息去重

为了保证数据一致性, 我们使用了多次重试操作, 这样就可能造成信息重复的问题, 重复消息一般来自两个方面
1 Provider没有MOM的响应信息, 会重复发送信息, 为了避免MOM处理重复信息, 我们可以使用一个标识来唯一标记一条信息, 这样在MOM接收到多条信息后就可以通过消息唯一标识去重;
2 MOM没有收到Consumer响应会重试, 这样Consumer可能会收到多条信息, 我们同样可以按照Provider处理, 或者在开发的时候将Consumer业务处理做幂等, 这样多次消费重复的消息不会影响业务数据, 但是可能会浪费资源(数据库更新操作).

特殊场景功能

针对特殊场景, 需要MOM提供些额外的功能

消息顺序性

一般情况下, 先发出去的信息会先处理, 但是通常由于网络等外部因素不会保证一定是顺序的, 而且每个Consumer只关心一条业务线的顺序, 比如支付流程, 先处理订单, 再支付, 这个顺序需要保证, 否则支付永远成功不了. 而不同的两笔支付, 先处理哪个就无所谓了, 也就是说MOM要保证消息的局部顺序性.
为了保证局部顺序性, 首先要标记消息为一条业务线, 然后每个消息的顺序, 还要区分每组信息(需要看看框架都是怎么实现的).

消息级别

这个很好理解, 对于比较重要的信息, 需要优先处理

总结

本篇是对MOM的一个整体认识, 后面会分析主流MOM是如何实现的.
1 消息通信模型
2 集群模式: 增删node处理
3 消息去重
4 消息顺序性
5 消息级别
6 其他
下一篇是RabbitMQ, 2020-01-09
终于水完了一篇~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章