这是我参与「第五届青训营」伴学笔记创作活动的第10天
为什么要使用消息队列? 消息队列使用场景:异步、解耦、调峰
异步:比如审批场景,需要一个流程完成多个步骤。 比如通过/拒绝后,需要发送消息通知,代理,通知下游系统。 如果这些步骤同步执行,整个调用链太长,耗时太长,影响整体性能。 因此消息通知、代理、通知下游的步骤可以异步处理。
解耦:如果使用多线程进行异步,会造成与下游系统的耦合。 每次连接一个系统,都会添加一个接口调用,然后重新发布系统。 使用消息队列将消息发布到消息队列,下游系统直接监听审批流程消息,感知审批进度,达到解耦的目的。
调峰:主要是应对突发流量冲击系统,导致系统过载。 消息队列可以控制消费速度,根据系统的处理能力进行消费,比如秒杀系统。
使用消息队列引起的一些问题 增加系统复杂度:引入一个中间件必然会增加系统的复杂度,还要考虑消息的重复消费、消息丢失、消息的顺序消费等。 数据一致性:数据一致性是分布式系统固有的问题,在消息队列中也是如此。 在审批过程中,如何保证消息、代理、下游系统的处理都成功? 如何保证数据一致性呢? 通过分布式事务可以保证数据的一致性。 降低可用性:当引入中间件时,必然会出现中间件故障,因此也需要保证中间件的可用性。 JMS 与 AMQP JMS:JAVA Message Service,java消息服务
jms是java消息服务的缩写,jms客户端可以使用jms服务进行异步消息传输。 ActiveMQ是基于JMS规范实现的。
jms 支持两种消息模型:
点对点 (P2P) 模型 发布/订阅(Pub/Sub)模型 JMS 五种不同的消息体格式:
StreamMessage -- Java 原始值流 MapMessage -- 一组名称-值对 TextMessage - 字符串对象 ObjectMessage - 序列化的 Java 对象 BytesMessage--字节流
AMQP: Advanced Message Queuing Protocol,高级消息队列协议
对比:
| JMS | AMQP | |
|---|---|---|
| 定义 | java API | 协议 |
| 跨语言 | 否 | 是 |
| 跨平台 | 否 | 是 |
| 支持的消息类型 | P2P、Pub/Sub | 提供了五种消息模型:①direct exchange;②fanout exchange;③topic change;④headers exchange;⑤system exchange。本质来讲,后四种和 JMS 的 pub/sub 模型没有太大差别,仅是在路由机制上做了更详细的划分 |
| 支持消息类型 | 支持多种消息类型 ,我们在上面提到过 | byte[](二进制) |
常见消息队列对比:
| 特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafka |
|---|---|---|---|---|
| 单机吞吐量 | 万级 | 万级 | 10万级 | 10万级 |
| topic数量对吞吐量的影响 | topic可以达到几百/几千级别,吞吐量小幅下降 | topic从几十到几百,吞吐量会大幅下降 | ||
| 时效性 | ms级 | 微妙级 | ms级 | ms级以内 |
| 可用性 | 高,基于主从架构 | 高,基于主从架构 | 非常高,分布式架构 | 非常高,分布式架构 |
| 消息可靠性 | 有较低概率丢失数据 | 基本不丢 | 经过参数优化配置,可以做到不丢失 | 经过参数优化配置,可以做到不丢失 |
| 功能支持 | MQ领域的功能及其完备 | 基于erlang开发,并发能力很强,性能极好,延时很低 | MQ功能较完善,分布式,扩展性好 | 功能较简单,主要支持简单的MQ功能,在大数据领域的实时计算及日志采集被大规模使用 |
| 社区活跃度 | 低 | 中 | 高 | 高 |
消息队列选型:
- ActiveMQ 和 RabbitMQ 相对于 RocketMQ 和 Kafka 吞吐量低一个数量级别,ActiveMQ 和 RabbitMQ 单机支持万级吞吐量,而 RocketMQ 和 Kafka 单机支持10万级吞吐量。
- RocketMQ topic 数量可以达到几百/几千级别,吞吐量小幅下降,Kafka 的 topic 数量从几十到几百,吞吐量会大幅下降
- RabbitMQ 时效性最高,是微妙级别。其次是 Kafka,是ms以内, ActiveMQ 和 RocketMQ 都是ms级的
- ActiveMQ 和 RabbitMQ 是基于主从架构,可用性高, RocketMQ 和 Kafka 是分布式架构,可用性非常高
- ActiveMQ 有较低概率丢失数据,RabbitMQ 基本不丢消息,RocketMQ 和 Kafka 可以通过参数优化做到不丢消息
- ActiveMQ 的功能最完备,RabbitMQ 的并发能力强,性能极好,延时很低,RocketMQ 功能较完善,Kafka 功能较简单,主要支持简单的MQ功能,在大数据领域的实时计算及日志采集被大规模使用
- RocketMQ 和 Kafka 社区活跃度高,ActiveMQ 和 RabbitMQ 社区活跃度较低
选型是需要综合考虑:可靠性、性能、吞吐量、消息有序性、是否会丢失消息等
消息队列之重复消费、顺序消费、分布式事务:
重复消费: 消费者集群如果重复消费同一条消息会导致业务异常,为了解决这个问题,我们需要保证接口幂等来消除重复消费引起的问题,幂等即在多次执行相同操作,产生的影响与第一次执行相同。
对于强校验的场景,我们可以加流水对账表来保证幂等性,即用流水对账表记录被消费的消息,如果再次来消息,先对比有没有被消费过,消费过的数据,直接返回。
对于弱校验场景,可以做缓存对比判断。
顺序消费: 对于同一个场景中多个消费者多条消息需要进行顺序消费的情况下,例如增改删操作需要保证顺序消费。或者审批流程的发起、通过、结束消息需要严格保证顺序。RocketMQ topic 内的队列机制可以保证 FIFO ,实现方式就是将消息同步发送到同一个队列中。此处只保证发送有序,消费有序由消费者自己保证。
分布式事务: 事务,是保证一系列操作,要么都成功,要么都回滚。分布式事务是在分布式系统中,保证一系列操作,要么都成功,要么都失败。
常见的分布式事务解决方案:
- 2pc(两阶段提交),MySQL bin.log 和 redo.log提交
- 3pc(三阶段提交)
- TCC(Try、Confirm、Cancel)
- 最大努力通知
- XA
- 本地消息表(ebay研发出的)
- 半消息/最终一致性(RocketMQ)