使用场景
解耦:消息存储在消息队列中,就算各个服务系统宕机了,消息也不会丢失
异步:生产消息发布到队列后生产者端可以继续做其他事。可加快系统的访问速度,提供更好的客户体验。
削峰:部分接口在特定时间,例如秒杀,可能存在短时间大量访问。通过消息队列可以让系统按照自己的最大处理能力去处理请求。
重复消费
生产者端可以使用存储messageId来避免重复发送。消费端由于存在一系列事务(拉取消息,业务处理,响应消息)可能会存在宕机需要重复处理的场景。所以只能由消费端自己管理,可以利用redis缓存的方式记录消息处理进度。
消息丢失
生产者和消费者端只要做好异常重试处理,消息不会丢失。存储队列防丢可以通过集群部署来实现,例如kafka就是在生产消息时会发布到所有节点来防止丢失。
定位消息丢失问题先从Provider开始,排查ack模式,ack=0时,消费者不会等待broker响应就会认为消息发送成功了,ack=1时,会等待learder broker响应才认为发送成功,ack=-1时会等待所有broker响应才认为发送成功。provider还可以设置retries等参数来控制重试次数和重试间隔。
排查broker有没有丢失消息可以通过分析server.log查看对应消息的topic和producerID。
消费者端消息丢失一般是因为发生异常时提前提交了offset导致重试时跳过了异常的消息。所以要确保消费者不要将autoCommit设置为true,有可能在异常前提交了offset。
可靠性
持久化:rocketMq可以将消息持久化到磁盘,增强可靠性,防止宕机造成的消息丢失。
确认+重试机制:消费者在消费完消息后需要像消息队列发送确认响应,否则消息队列会重新发送消息给消费者
顺序性
首先需要确认哪些消息时必须要按照顺序的,因为顺序会限制并发,所以要在性能和顺序间做取舍。kafka通过partition分区的方式来保证同一分区内的消息都是按顺序消费的。
消息积压
对于一个运行稳定经历过压测的系统,一般是消费者端的BUG导致了消费积压。首先就要紧急扩容:先定位出来消费者端的BUG,解决之后先停掉所有的消费者。然后新建一个Topic,partition是原来的十倍,临时建立原来10倍的消息队列数量。然后写一个临时的消息分发程序,来作为消费者将消息队列中的消息分发的新的Topic下的队列中。然后临时征用原来10倍的资源来部署修复后的消费者程序去消费消息,压力解除后恢复原有架构。
消息队列实现分布式事务
KAFKA
kafka使用的是拉取策略, 即由消费者维护消费状态,例如队列下标等。这样做可以根据consumer的性能来来消费消息,避免阻塞。一个消费者组可以同时消费topic下的多个partition。
kafka为什么这么快?
顺序写入优化:将消息按顺序写入磁盘而不是随机的,这样在消息写入寻址时更快。
批量处理:支持生产者积累到一定量的消息后一次性发送给broker,降低了网络开销和IO。(生产者维护一个buffer,消息会在buffer中积累(按分区隔离))
零拷贝:可以直接将数据从磁盘写入到网络套接字socket里,减少上下文切换
压缩:支持压缩消息大小,降低网络开销