消息队列
协议
JMS
-
代表:ActiveMQ
-
消息模型
- 点对点队列
- 发布订阅
-
消息格式
- StreamMessage
- MapMessage
- TextMessage
- BytesMessage
- ObjectMessage
-
AMQP
-
代表:RabbitMQ
-
消息模型
- direct exchange直连完全匹配
- fanout exchange广播通知所绑定的
- topic exchange可以模糊匹配
- headers exchange
- system exchange
-
消息格式
- Byte[]
-
使用案例
-
创建一个队列,创建一个交换器,定义一个路由
- BindingBuilder.bind(demo01Queue()).to(demo01Exchange()).with(Demo01Message.ROUTING_KEY)
-
生产者把消息放到队列
-
消费者监听某个队列执行对应的Handler进行消费,使用注解@RabbitListener和@RabbitHandler
-
路由规则
-
通配符*和#的区别
- 通配符 * ,代表一个占位符,或者说一个单词,比如路由为 user.*,那么 user.email 可以匹配,但是 user.aaa.email 就匹配不了
- 通配符 # ,代表一个或多个占位符,或者说一个或多个单词,比如路由为 user.#,那么 user.email 可以匹配,user.aaa.email 也可以匹配
-
-
批量
- 发送
- 消费
-
消费与发送重试
- 在消息消费失败的时候,Spring-AMQP 会通过消费重试机制,消费重试之后还失败进入死信队列
-
-
定时消息
- 死信队列 + TTL
- Delayed Message Plugin插件,提供更加通用的定时消息的功能(推荐)
-
消息模式
-
集群消费
- 配置多个消费实例,监听同一个Queue,自己的理解
-
广播消费
- 我的理解fanout模式
-
-
并发消费
- concurrency配置
-
顺序消息
-
做两件事
-
保证 RabbitMQ Producer 发送相关联的消息发送到相同的 Queue 中
-
有且仅启动一个 Consumer 消费该队列,保证 Consumer 严格顺序消费
-
两个问题
-
消息积压的问题
- 将 Queue 拆成多个子 Queue
- 将相关联的消息发送到相同的线程中来消费
-
启动相同 Consumer 的多个进程
- 引入 ZooKeeper 来协调,动态设置多个进程中的相同的 Consumer 的开关,保证有且仅有一个 Consumer 开启对同一个 Queue 的消费。
-
-
-
-
重复消费
- 消息生产时,MQ 内部针对每条生产者发送的消息生成一个 inner-msg-id ,作为去重和幂等的依据
- 在消息消费时,要求消息体中必须要有一个 bizId(对于同一业务全局唯一,如支付 ID、订单 ID、帖子 ID 等)作为去重和幂等的依据,避免同一条消息被重复消费。
-
事务消息
- 开启 RabbitMQ 事务channel.txSelect
- 发送消息
- channel.txCommit/channel.txRollback(回滚或者提交事务)
-
消息丢失
-
生产者
- 开启RabbitMq事务(同步,不推荐)
- 开启Confirm模式(异步,推荐)
-
MQ
-
开启RabbitMq持久化
- 创建 queue 的时候将其设置为持久化
- 发送消息的时候将消息的 deliveryMode 设置为 2
就是将消息设置为持久化的,此时 RabbitMQ 就会将消息持久化到磁盘上去
-
-
消费者
- 关闭RabbitMq的自动ACK,改为手动ACK
-
-
消息堆积问题
-
队列上绑定多个消费者,提高消费速度
-
给消费者开启线程池,提高消费速度
-
使用惰性队列,可以再mq中保存更多消息
- 惰性队列基于磁盘存储,消息上限高
- 没有间歇性的page-out,性能比较稳定
-
-
消息确认
-
生产者
-
Confirm 模式
- 同步
- 异步
-
-
消费者(Ack模式)
- 自动确认
- 手动确认
-
-
RPC 远程调用
-
MessageConverter
- Java POJO 与AMQP Message 互转
-
消费异常处理器
-
message 被可靠持久化的条件是 queue 和 exchange 具有 durable 属性,同时 message 具有 persistent 属性才行
-
高可用
-
单机模式
-
普通集群模式
- 会在集群的各个节点间共享部分数据,包括:交换机、队列元信息。不包含队列中的消息。
- 当访问集群某节点时,如果队列不在该节点,会从数据所在节点传递到当前节点并返回
- 队列所在节点宕机,队列中的消息就会丢失
-
镜像集群模式(推荐)
-
交换机、队列、队列中的消息会在各个mq的镜像节点之间同步备份。
-
创建队列的节点被称为该队列的主节点,备份到的其它节点叫做该队列的镜像节点。
-
一个队列的主节点可能是另一个队列的镜像节点
所有操作都是主节点完成,然后同步给镜像节点
主宕机后,镜像节点会替代成新的主 -
三种模式
-
exactly(推荐)
- count=镜像数量+1,count=2表示一个队列主和一个队列镜像,推荐count=N/2+1
-
all
- 所有节点进行进行镜像
-
nodes
- 指定队列创建到哪些节点,如果指定的节点全部不存在,则会出现异常
-
-
仲裁队列,3.8以后才有的新功能,用来替代镜像队列,
- 与镜像队列一样,都是主从模式,支持主从数据同步
使用非常简单,没有复杂的配置
主从同步基于Raft协议,强一致
- 与镜像队列一样,都是主从模式,支持主从数据同步
-
-
-
Broker
- 指一个或多个 erlang node 的逻辑分组,且 node 上运行着 RabbitMQ 应用程序
-
Cluster
- 是在 Broker 的基础之上,增加了 node 之间共享元数据的约束
-
-
主要分层
-
功能层
- 定义一系列命令
-
传输层
- 携带了从应用 → 服务端的方法,用于处理多路复用、分帧、编码、心跳、data-representation、错误处理。
-
-
主要组件
-
exchange(交换器)
- 从Publisher程序中收取消息,并把这些消息根据一些规则路由到消息队列(Message Queue)中
-
message queue(消息队列)
- 存储消息。直到消息被安全的投递给了消费者
-
binding
- 定义了 message queue 和 exchange 之间的关系,提供了消息路由的规则。
-
满足高性能
优势:异步、削峰、解耦(发布订阅模式)
劣势:可用性、复杂性、一致性
常见对比方向
吞吐量
- (Rocket、Kafka)十万百万)>(Active、Rabbite)万
可用性
- ActiveMQ 和 RabbitMQ主从架构
- Rocket和Kafka基于分布式架构
时效性
- Rabbite 微秒级 其他三个毫秒级
丢失性
- ActiveMQ 和 RabbitMQ 丢失的可能性非常低, RocketMQ 和 Kafka 理论上不会丢失。
功能性
- Kafka功能简单,其他三个功能较为完备
消息持久化
分布式KV存储
- 这类 MQ 一般会采用诸如 LevelDB 、RocksDB 和 Redis 来作为消息持久化的方式
文件系统
- 目前业界较为常用的几款产品(RocketMQ / Kafka / RabbitMQ)均采用的是消息刷盘至所部署虚拟机/物理机的文件系统来做持久化(刷盘一般可以分为异步刷盘和同步刷盘两种模式)
关系型数据库 DB
- Apache下开源的另外一款MQ—ActiveMQ(默认采用的KahaDB做消息存储)可选用 JDBC 的方式来做消息持久化,通过简单的 XML 配置信息即可实现JDBC消息存储。