1.基本概念
MQ(Message Queue,消息队列)是一种用于在不同系统、服务或组件之间异步传递消息的中间件。常用的 Dubbo,Http 协议都是同步的。这两种协议很难实现双端通讯,而且不支持长连接。MQ 做的就是在这些协议上构建一个简单协议——生产者、消费者模型,发送数据的叫做生产者,接受消息的叫做消费者。
2.应用场景
2.1异步
以电商场景为例,下单时候分为几个步骤:下单支付、库存扣除、短信通知,如果在一个流程中走完,需要一个步骤一个步骤执行,流程的响应时间会随之增长。随着业务不断开发,可能还会有积分扣除、优惠券折算等步骤加入流程中,最终会导致接口的响应时间变得很长,用户体验变差。
为了解决上面的问题,可以引入异步的思想,让上面的几个步骤同时进行,支付成功后,在库存扣减的同时进行短信通知、积分扣除等操作,让串行执行变成并行执行。
2.2解耦
还是以电商场景为例,大型电商一般采用的是微服务架构,会将电商系统分为交易、商品、购物车、订单等模块。在上面的下单流程中,下单之后订单模块需要通知库存模块,即订单模块需要调用库存模块的接口。这样订单模块和库存模块就出现耦合了,一旦库存模块出现故障了,订单模块就会调用失败,导致下单失败。
这个时候需要引入消息队列,订单模块作为生产者,库存模块作为消费者。订单模块将库存扣减的消息写入消息队列,让库存模块去订阅并消费,即使库存模块出现故障,后期也可以通过数据补偿或者分布式事务去解决。
2.3削峰
又是以电商场景为例,经常会有那种双11秒杀活动,活动开始会有大量的流量同时打进来,服务器承受能力不够的话会直接导致应用挂掉。为了防止服务器挂掉,引入消息队列是最合适的选择。把大量的请求放入消息队列里面,等着后面的服务批量消费,可以缓解短时间的高流量压垮应用。
3.缺点
因为 MQ 是个消息中间件,引入之后不可避免的是系统复杂性增加,维护成本变高,而且使用的过程需要考虑各种问题:消息堆积、重复消费、消息丢失、顺序消费、一致性等。
3.1消息堆积
生产者的生产速率远远大于消费者的消费速率,使消息大批量堆积在消息队列。
解决方案如下:
1.增加消费者集群,提高消费者的消费速率
2.消费者通过多线程分批处理
3.限流
3.2重复消费
生产者产生了两条一模一样的消息或者消费者一条消息消费了多遍。
由于网络波动或其他原因,消费者发生异常消费失败,消费者会通知生产者重新发送消息,此时就有可能发生重复消费的问题。
解决方案如下:
幂等性校验,通过全局唯一ID + 业务标识、分布式锁保证唯一性,保险起见给数据库加唯一索引。比较常见的是支付场景,在流水表中生成一条流水,消息一来先拿着订单号+业务场景这样的唯一标识去流水表里面查,如果有这条流水,后面的流程也不需要执行了。
3.3消息丢失
网络问题或者消息中间件挂了导致消息丢失。
3.4顺序消费
在同个业务场景下发不同几个操作的消息同时过去,本身顺序是对的,但是消费的时候顺序乱掉了,比如在数据库同时对一个 ID 的数据进行了增、改、删三个操作,但是消息发过去后消费时变成了改,删、增。可使用 RocketMQ 保证顺序发送。
4.常用消息中间件
目前常用的消息中间件有 ActiveMQ、RabbitMQ、RocketMQ、Kafka等。
特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafka |
单机吞吐量 | 万级 | 万级 | 10万级 | 10万级 |
时效性 | ms级 | μs级 | ms级 | ms级内 |
可用性 | 高,基于主从架构实现高可用性 | 高,基于主从架构实现高可用性 | 非常高,分布式架构 | 非常高,kafka是分布式的,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用 |
消息可靠性 | 有较低的概率丢失数据 | 可靠性是非常好的,数据能够保证百分之百的不丢失 | 经过参数优化配置,可以做到0丢失 | 经过参数优化配置,可以做到0丢失 |
功能支持 | MQ领域的功能极其完备 | 并发能力很强,性能极其好,延时很低 | MQ功能较为完善,还是分布式的,扩展性好 | 功能较为简单,主要支持简单的MQ功能,在大数据领域的实时计算以及日志采集被大规模使用,是事实上的标准 |
topic数量对吞吐量的影响 | topic可以达到几百,几千个的级别,吞吐量会有较小幅度的下降。这是RocketMQ的一大优势,在同等机器下,可以支撑大量的topic | topic从几十个到几百个的时候,吞吐量会大幅度下降所以在同等机器下,kafka尽量保证topic数量不要过多。如果要支撑大规模topic,需要增加更多的机器资源 |