RabbitMQ,RocketMQ,Kafka 消息模型对比分析
消息模型
消息队列的演进
消息队列模型
早起的消息队列是按照"队列"的数据结构来设计的。
生产者(Producer)产生消息,进行入队操作,消费者(Consumer)接收消息,就是出队操作,存在于服务端的消息容器就称为消息队列。
当然消费者也可能不止一个,存在的多个消费者是竞争的关系,消息被其中的一个消费者消费了,其它的消费者就拿不到消息了。
发布订阅模型
如果一个人消息想要同时被多个消费者消费,那么上面的队列模式就不适用了,于是又引出了一种新的模式,发布订阅模型。
在发布-订阅模型中,消息的发送方称为发布者(Publisher),消息的接收方称为订阅者(Subscriber),服务端存放消息的容器称为主题(Topic)。
发布者发送消息到主题中,然后订阅者需要先订阅主题。订阅主题的订阅者之后就可以收到发送者发送的消息了。
发布订阅也是兼容消息队列模型的,如果只有一个订阅者,就是消息队列模型了。
RabbitMQ的消息模型
RabbitMQ 使用的还是消息队列这种消息模型,不过它引入了一个 exchange 的概念。
exchange 也就是交换器,位于生产者和队列之间,生产者产生的数据是直接发送到 exchange 中,然后 exchange 根据配置的策略将消息发送到对应的队列中。
RabbitMQ 中通过绑定将交换器和队列关联起来,绑定的时候一般会指定一个绑定键(BindingKey)。
生产者发送消息的时候会指定一个 RoutingKey ,当 RoutingKey 和 BindingKey,一样的时候就会被发送的对应的队列中去。
交换器的类型
RabbitMQ 中肠常用的交换器有 fanout、direct、topic、headers
四种,这里来一一分析下
direct
direct 会根据发送消息的 RoutingKey ,然后发送到和 RoutingKey 匹配的 BindingKey 对应的队列中去。
如果发送消息的路由键也就是 RoutingKey,为 log 的时候,两个消息队列都会收到消息,如果路由键为 debug ,exchange 只会把消息发送到消息队列1中。
topic
direct 中的 RoutingKey 和 BindingKey 是完全匹配才能发送消息,topic 中在此基础之上做了扩展,也就是引入了模糊匹配机制。
RoutingKey 和 BindingKey 中使用 . ,来分割字符串,被 . 分割开的每一段字符串就是一个匹配字符;
BindingKey 中主要通过 * 和 # ,用于模糊匹配,* 表示一个单词,# 代表任意0个或多个单词;
BindingKey 中单独使用 # 时,会接收所有的消息,这与类型 fanout一致;
栗子:
1、路由键为 test.rabbitmq
消息队列1和消息队列2都会收到消息;
2、路由键为 rabbitmq
没有队列能收到消息;
3、路由键为 test
消息队列2会收到消息;
4、路由键为 rr.info.ww
消息队列2会收到消息;
5、路由键为 info
没有队列能收到消息;
fanout
该交换器收到的信息会被发送到所有与改交换器绑定的队列中。
headers
headers 类型的交换器不依赖于路由键的匹配规则来路由消息,而是根据发送的消息内容中 headers 属性进行匹配。在绑定队列和交换器时制定一组键值对当发送消息到交换器时,RabbitMQ 会获取到该消息的 headers (也是一个键值对的形式) ,对比其中的键值对是否完全匹配队列和交换器绑定时指定的键值对,如果完全匹配则消息会路由到该队列,否则不会路由到该队列 headers 类型的交换器性能会很差,而且也不实用,基本上不会看到它的存在。
Kafka的消息模型
Kafaka 中引入了一个 broker。broker 接收生产者的信息,为消息设置偏移量,并且保存的磁盘中。broker 为消费者提供服务,对读取分区的请求作出响应,返回已经提交到磁盘上的消息。
同时 broker 也会对生产者和消费者进行消息的确认。
生产者发送消息到 broker,如果没有收到 broker 的确认就可以选择继续发送;
消费者同理,在消费端,消费者在收到消息并完成自己的消费业务逻辑(比如,将数据保存到数据库中)后,也会给服务端发送消费成功的确认,broker 只有收到消费确认后,才认为一条消息被成功消费,否则它会给消费者重新发送这条消息,直到收到对应的消费成功确认。
如果一个主题中,每次只有一个消费实例在处理,同时我们也要保持消息的有序性,当前消息没有被消费掉就不能接着消费下一个消息。那么,消费的性能将是极低的,这时候引入了一个分区的概念。
主题可以被分为若干个分区,一个分区就是一个提交日志。消息以追加的方式写入分区,然后以先入先出的顺序读取。要注意,由于一个主题一般包含几个分区,因此无法在整个主题范围内保证消息的顺序,但可以保证消息在单个分区内的顺序。
同时引入了消费者组,消费者是消费者组中的一部分,这样会有一个或者多个消费者读一个分支,不过群组会保证一个分区只能被一个消费者消费,通过多消费者,这样消费的性能就提高了。
每个消费组都消费主题中一份完整的消息,不同消费组之间消费进度彼此不受影响,也就是说,一条消息被Consumer Group1
消费过,也会再给Consumer Group2
消费。不过同组内是竞争关系,同组内一个消息只能被同组内的一个消息消费。
消费者通过偏移量来确认读过的数据,他是个不断累加的数据,每次成功消费一个数据这个偏移量就加一。在给定的分区中,每个消息的偏移量都是唯一的。消费者会把每个分区读取的消息偏移量保存在 Zookeeper 或 Kafka 上,如果消费者关闭或重启,它的读取状态不会丢失。
RocketMQ的消息模型
RocketMQ 中的消息模型和 Kafaka 类似,把 Kafaka 中的分区换成队列,就是 RocketMQ 的消息模型了。
不过虽然消息模型类似,但是实现方式还是有很大的差别的。
参考
【消息队列高手课】https://time.geekbang.org/column/intro/100032301
【消息队列设计精要】https://tech.meituan.com/2016/07/01/mq-design.html
【RabbitMQ实战指南】https://book.douban.com/subject/27591386/
【Kafka权威指南】https://book.douban.com/subject/27665114/
【RabbitMQ,RocketMQ,Kafka 消息模型对比分析】https://boilingfrog.github.io/2021/12/18/几种消息队列的消息模型/
最新文章
- thinkphp 3.2.3 动态修改conf配置文件
- er3
- GitHub &; Bitbucket &; GitLab &; Coding 的对比分析
- .net 使用Json(),maxJsonLength属性设置的值问题
- 简单的cookie使用
- Django CRM __contains与__icontains区别
- Tensorflow 的Word2vec demo解析
- Visual Stuido 在文件中查找不显示结果
- 1.saltstack基础笔记
- Linux系统架设支持自助开通Shado wsocks及VPN前端的教程
- Windows-1252对Latin1编码有改变(并不完全兼容),而且Latin1缺失了好多西欧字符(法语,德语,西班牙语都有)
- FPGrowth
- dotnet core使用开源组件FastHttpApi进行web应用开发
- BZOJ3291Alice与能源计划——匈牙利算法+模拟费用流
- java 打印水仙花数
- LeetCode 1012 Complement of Base 10 Integer 解题报告
- Boost--variant (C++中的union)
- RabbitMQ(二):理解消息通信RabbitMQ
- 窗口启用/禁用功能函数EnableWindow的使用
- SqlServer SqlBulkCopy批量插入 -- 多张表同时插入(事务)
热门文章
- [atARC125F]Tree Degree Subset Sum
- vip视频解析保存
- 为了拿捏 Redis 数据结构,我画了 40 张图(完整版)
- SUNTANS 及 FVCOM 对流扩散方程求解简介[TBC]
- vim——批量缩进
- Oracle-常用表的查询、增加列、删除列、修改列值功能【增删改查】
- mysql数据操作语言DML
- Identity Server 4 从入门到落地(七)—— 控制台客户端
- Flink(二)【架构原理,组件,提交流程】
- 大数据学习day24-------spark07-----1. sortBy是Transformation算子,为什么会触发Action 2. SparkSQL 3. DataFrame的创建 4. DSL风格API语法 5 两种风格(SQL、DSL)计算workcount案例