消息队列,如何降低消息队列系统中消息的延迟
当项目中引入了消息队列时,增加了系统的复杂度。需要考虑如何保证消息不会丢失、如何避免消息重复带来的影响、还需要考虑消息延迟情况,即消费性能的问题。
场景:上游服务器产生订单数据,并将数据放入消息队列服务中,但是下游系统消费较慢,造成消息队列出现大量堆积。下游系统处理消息出现了延迟。
如何来解决这个问题呢?
- 监控消息的延迟
- 减少消息延迟的正确做法
1 如何监控消息延迟
监控消息的延迟有两种方式:
- 使用消息队列提供的工具,通过监控消息的堆积来完成监控
- 通过生成监控消息的方式来监控消息的延迟情况
1.1 使用消息队列提供的工具
1、在 Kafka 中,提供了 ”kafka-consoumer-groups.sh“ 在 bin 目录下。
通过命令查看消息积累情况
./bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --describe --group test-consumer-group
2、使用工具 JMX
Kafka 通过 JMX 暴露了消息堆积的数据,我在本地启动了一个 console consumer,然后使用 jconsole 连接 consumer 就可以看到 consumer 的堆积数据了
1.2 通过生成监控消息的方式监控消息的延迟
1、定义一个特殊的信息,
2、启动一个监控程序将这个消息定时地循环写入到消息队列中
3、消息的内容可以是生成消息的时间戳并作为队列的消费这消费数据
4、业务处理程序消费到这个数据时可以直接丢弃掉
5、监控程序在消费到这个消息就可以和消息的生成时间做比较
6、如果时间差达到某一个阀值就报警
2 减少消息延迟的正确做法
减少消息的处理演示,可以在消费端和消息队列自身两个地方进行优化。
2.1 在消费端提升
- 优化消费代码提升消费性能
- 增加消费者数量(Kafka不支持)
如果不能添加 consumer ,可以在一个 consumer 中提升消息的并行度,采用多线程来进行处理,以便提高消息消费的吞吐量。
2.2 消息队列
消息队列本身在读取性能优化方面可以考虑
- 消息的存储
- 零拷贝技术
消息存储,普通的数据库来存储消息,但是受限于数据库的性能瓶颈,如果使用本地磁盘作为存储介质。Page Cache 的存储就可以提升消息的读取速度。
零拷贝技术,尽量较少拷贝的次数。
在读取消息队列的数据时,其实就是把磁盘中的数据通过网络发送给消费客户端,涉及了4次数据拷贝步骤:
- 数据从磁盘拷贝到内核缓冲区;
- 系统调用将内核缓存区的数据拷贝到用户缓冲区;
- 用户缓冲区的数据被写入到 Socket 缓冲区中;
- 操作系统再将 Socket 缓冲区的数据拷贝到网卡的缓冲区中。
操作系统提供了 Sendfile 函数可以减少数据被拷贝的次数。使用了 Sendfile 之后,在内核缓冲区的数据不会被拷贝到用户缓冲区而是直接被拷贝到 Socket 缓冲区,节省了一次拷贝的过程提升了消息发送的性能。高级语言中对于 Sendfile 函数有封装,比如说在 Java 里面的 java.nio.channels.FileChannel 类就提供了 transferTo 方法提供了 Sendfile 的功能。