定义
消息队列(Message Queue)是一种应用间的通信方式,消息发送后可以立即返回,由消息系统来确保消息的可靠传递。消息发布者只管把消息发布到 MQ 中而不用管谁来取,消息使用者只管从 MQ 中取消息而不管是谁发布的。这样发布者和使用者都不用知道对方的存在。
适用场景
异步处理
减少冗长的业务代码(和异步线程相比),降低响应时间,提高系统吞吐量。
解耦
如上图所示,如果不使用消息队列,代码中会内嵌很多其他系统的业务逻辑,每次加一个要调用的接口然后还要重新发布系统,问题排查也麻烦。只需要将资讯发布成功的消息写入消息队列即可,其他系统负责监听。
流量削锋
如果在某些时刻,流量突然大量涌进,服务器、Redis、MySQL都可能挂掉。可以把请求放到队列里面,然后至于每秒消费多少请求,就看自己的服务器处理能力,可能会比正常的慢一点,但是不至于打挂服务器,等流量高峰下去了,服务也就没压力了。
日志处理
日志处理是指将消息队列用在日志处理中,解决大量日志传输的问题。
缺陷
重复消费
消息丢失
消息的顺序消费
数据一致性
分布式事务
常见MQ对比
RabbitMQ:性能好,延时低,管理界面好用,社区活跃。但是吞吐量比较低,erlang语言实现,不好进行进一步开发扩展。
RocketMQ:接口简单易用,源码是阿里出品,可自定义MQ。但是社区活跃度一般,万一不维护,需要自己公司研发。
Kafka:仅仅提供较少的核心功能,但是具备超高的吞吐量和ms级的延迟,极高的可用性和拓展性。但是存在消息重复消费的缺点,适合于大数据实时计算和日志收集。
常见问题
大量消息在mq里积压了几个小时了还没解决
几千万条数据在MQ里积压了七八个小时,如果你积压了几百万到上千万的数据,即使消费者恢复了,也需要大概1小时的时间才能恢复过来。
一般这个时候,只能操作临时紧急扩容了,具体操作步骤和思路如下:
- 先修复consumer的问题,确保其恢复消费速度,然后将现有consumer都停掉。
- 新建一个topic,partition是原来的10倍,临时建立好原先10倍或者20倍的queue数量。
- 然后写一个临时的分发数据的consumer程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的10倍数量的queue。
- 接着临时征用10倍的机器来部署consumer,每一批consumer消费一个临时queue的数据。
- 这种做法相当于是临时将queue资源和consumer资源扩大10倍,以正常的10倍速度来消费数据。
- 等快速消费完积压数据之后,恢复原先部署架构,重新用原先的consumer机器来消费消息。
如何保证消息不被重复消费/如何保证消息消费的幂等性
消费者在消费消息的时候,消费完毕后,会发送一个确认消息给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息队列中删除。而因为网络传输等等故障,确认信息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将消息分发给其他的消费者。
常见解决场景:
拿到这个消息做数据库的insert操作
给这个消息做一个唯一的主键,那么就算出现重复消费的情况,就会导致主键冲突
你拿到这个消息做redis的set的操作
这种情况不用解决,因为你无论set几次结果都是一样的,set操作本来就算幂等操作
给消息分配一个全局id
在业务中通常是具备唯一业务标识的字符串,如:订单号、流水号等。且一般由生产者端生成并传递给消费者端。
消息丢失的处理
生产者阶段
不管是同步还是异步的方式,都会碰到网络问题导致发送失败的情况。针对这种情况,我们可以设置合理的重试次数,当出现网络问题,可以自动重试。
消息队列阶段
将消息保存机制修改为同步刷盘方式
集群部署方式采用一主(master)多从(slave)部署方式
默认采用异步的方式,为了进一步提高消息的可靠性,我们可以采用同步的复制方式,master节点将会同步等待 slave 节点复制完成,才会返回确认响应。
消费阶段
如果 Broker 未收到消费确认响应或收到其他状态,消费者下次还会再次拉取到该条消息,进行重试。