前言
可能你在没学消息中间件之前都已经听过很多概念了,JMS,AMQP,ActiveMQ,RabbitMQ,Kafka,RocketMQ,一个消息中间件怎么能搞出怎么多概念?本文从历史的角度帮你理清这些MQ和协议之间的关系。
什么是消息中间件?
消息中间件属于分布式系统中的一个子系统,关注于数据的发送和接收,利用高效可靠的消息传递机制对分布式系统中的其余各个子系统经进行集成
消息中间件的使用场景
1.异步处理
非核心流程异步化,提高系统响应性能
原来用户注册一下可能得依次写数据库,发送邮件和短信后,才能提示用户注册成功
现在只要写数据库,写消息队列后就直接提示用户注册成功,发送邮件和短信是异步处理,提高了响应速度
2.应用解耦
系统不是强耦合,消息接受者可以随意增加,而不需要修改消息发送者的代码。消息发送者的成功不依赖消息接受者
如果库存系统出了问题,用户就不能正常下单,这是不合理的。可以通过消息队列来解耦。
当有新的系统如广告系统对用户的订单也感兴趣的时候,只需要从消息队列中拿消息即可,订单系统完全不用改变
3.流量削峰
当上下游系统处理能力存在差距的时候,可以用消息队列进行缓冲
当有秒杀业务时,一下有大量请求涌入时,很可能造成系统瘫痪,此时可以用消息队列缓冲一下
4.日志处理
将消息队列用在日志处理中,比如Kafka可以用来解决大量日志传输的问题
5.消息通讯
消息队列一般都内置了高效的通信机制,因此也可以用于单纯的消息通讯,比如实现点对点消息队列或者聊天室等
消息中间件编年史![]()
初见曙光
1.消息中间件其实诞生的很早,在互联网应用还是一片荒芜的年代,有个在美国的印度哥们Vivek Ranadive就设想了一种通用软件总线,采用发布订阅的模式,像主板上的总线一样供其他相应程序接入。他创办了一家公司Teknekron,实现了世界上第一个消息中间件The Information Bus(TIB)
各自为战
2.TIB受到了企业的欢迎,Teknekron的业务发展引起了当时最牛气的IT公司IBM的注意,于是他们也开始研发了自己消息队列软件,于是才有了后来的wesphere mq,微软也陆续加入了战团。由于商业壁垒,商业MQ供应商想要解决应用应用互通的问题,而不是去创建标准来实现不同MQ产品间的互通,或者允许应用程序更改MQ平台
劫制天下
3.为了打破这个壁垒,同时为了能够让消息在各个消息队列平台间互融互通, JMS (Java Message Service) 应运而生 。 JMS 试图通过提供公共 Java API 的方式,隐藏单独 MQ 产品供应 商提供的实际接口,从而跨越了壁垒,以及解决了互通问题。从技术上讲, Java 应用程序只需 针对 JMS API 编程,选择合适的 MQ 驱动即可, JMS 会打理好其他部分 。 ActiveMQ 就是 JMS 的 一种实现 。 不过尝试使用单独标准化接口来胶合众多不同的接口,最终会暴露出问题,使得 应用程序变得更加脆弱 。 所以急需一种新的消息通信标准化方案 。
一统江湖
4.在 2006 年 6 月,由 Cisco 、 Redhat 、iMatix 等联合制定了 AMQP 的公开标准,由此 AMQP 登上了历史的舞台 。 它是应用层协议的一个开放标准,以解决众多消息中间件的需求和拓扑结 构问题 。 它为面向消息的中间件设计,基于此协议的客户端与消息中间件可传递消息,并不受 产品、开发语言等条件的限制 。
合久必分
5.LinkedIn在实现消息队列的时候觉得AMQP规范并不适合自己,所以Kafka并不支持AMQP协议。RocketMQ在实现上借鉴了Kakfa的思想,所以也不支持AMQP协议,并且你会发现在Kafka和RocketMQ中都有类似Topic和Consumer Group的概念,而这些概念在AMQP协议中是不存在的
消息模型
如果让你设计一个消息队列?你会怎么实现呢?
可能你立马就会想到用队列,一边放,一边取。这其实就是消息队列常见的一种消息模型,队列模型
所以一个简单的消息队列用Redis中的List就能实现。当然Redis5.0版本之后针对消息队列这种场景新增了专门设计了一个数据结构Streams。
队列模型有哪些缺点呢?
如果将一类消息发送给不同的消费者,每个消费者都要接收全量的消息,此时就很不方便。因为你要将相同的消息发送到不同的队列,多一个消费者就得多发一份队列。这样生产者必须知道有多少个消费者,不利于解耦。
那么该如何解决这个问题呢?
想想RabbitMQ的结构图
RabbitMQ并不是直接把消息发送到队列中的,而是发送到Exchange中,Exchange和Queue进行关联,消息由Exchange根据规则发送到对应的队列。这样生产者和消费者完成了解耦。
还有哪种方式能解决这种多消费者的问题呢?
答对了,就是发布订阅模型
RocketMQ和Kafka都是基于发布订阅模型实现的,RocketMQ的消息模型图如下
生产者是发布者,消费者是订阅者,消息是主题
为了提高消费的并行度,一类消息会被分发到多个队列中,在RocketMQ中叫Queue,在Kafka中叫做Partition(分区),都是类似的概念。
如何选择消息中间件
| 对比维度 | RabbitMq | Kafka | RocketMq |
|---|---|---|---|
| 核心定位 | 主打复杂路由,微秒级低延迟 | 分布式流处理平台,主打海量日志和高吞吐 | 金融级业务消息中间件,主打高并发与业务特性 |
| 开发语言 | Erlang | Scala/Java | Java |
| 单机吞吐量 | 万级(QPS) | 百万级(QPS) | 十万级(QPS) |
| 时延 | 微秒级(最快) | 毫秒级 | 毫秒级 |
| 消息堆积能力 | 弱 堆积后性能急剧下降 | 极强 磁盘顺序写,堆积无性能损耗 | 极强 |
| 功能支持 | 功能最丰富 支持复杂路由、延迟/死信/优先级队列 | 功能最简 原生不支持延迟/死信/重试队列 | 业务功能完备 原生支持事务/延迟/死信/重试队列 |
| 消费模式 | Push + Pull | Pull | Push + Pull |
| 适用场景 | 微服务通信、复杂路由、低延迟业务 | 日志收集、大数据管道、实时计算、用户行为分析 | 电商交易、金融核心、分布式事务、需要强一致性的业务 |