1、说明
1.1、场景
为了搞个支持高并发、低延时的基础消息平台,同时要支持js端,造成了现有开源软件选择面太窄,在评估了rabbmitmq、emqtt、activemq、rocketmq后,综合考虑选择了RabbitMQ作为消息中间件,为啥要选RabbitMQ呢,RabbitMQ社区活跃久经沙场、除了AMQP消息协议外支持通过插件方式同时支持MQTT、STOMP协议同时各协议能互通、高性能、支持集群等等综合性原因,选择了它。
在使用方面作为要支持网页端消息基础平台,由于RabbitMQ原生的AMQP协议官方不提供基于websocket的网页端支持,选择了使用MQTT协议作为消息通讯协议。在qos1的情况下,使用如下两种类测试用例:
测试结果表明单个topic的consumer客户端TPS达到1.6w/s时,大量的消息处于unconfirmed状态堵塞在服务端,导致publish发送消息堵塞,发送时延极速增加(达到10多秒),TPS此时已经达到上限无论是新增publish客户端还是新增consumer客户端都无法增加消费的TPS,如下:
由于要基于单个topic的消息路由瞬间大量并发低延时的消息平台来说,这点TPS是不够用,虽然此问题可以通过将消息分散到不同的topic来进行分散处理但是这增加了软件的复杂度和管理难度。
1.2、原因分析
之前有基于AMQP协议对RabbitMQ进行性能测试,上述场景单队列单consumer客户端情况下(非持久化消息)TPS能达到5w/s以上,跨机房消费时延95%以上处于0~50ms期间,同样的对于基于RabbitMQ的MQTT协议的支持,只是通过MQTT的插件将MQTT协议转换成RabbitMQ的AMQP协议进行处理,此类情况再相同测试环境和测试方式的情况下出现如此大的差距,只能是MQTT插件在协议转换中出现了导致测试条件不一致的情况。
分析发现MQTT插件源码在qos1的情况下会将消息delivery_mode设置为2,意思是对消息进行写入磁盘持久化,消息持久化限制消息confirm的速度导致如上问题,MQTT插件这样做的做法也可以理解,qos1表示消息最少到达一次,消息持久化的情况下即使RabbitMQ服务器崩溃也能保证消息的不丢失,保证了qos1的特性。
但是自己实际使用的场景中消息是有时效性的,在RabbitMQ消息集群中某台服务器挂了,等这台服务器恢复后这些持久化的消息已经失效,不具备可用性了,所有在使用的这类场景中不需要执行严格的qos1,不需要对消息进行持久化,只需将消息存储在内存中就好了。所以开始动手开始修改MQTT插件源码重新编译安装。
2、MQTT插件修订
1.从https://github.com/rabbitmq/rabbitmq-mqtt/tree中下载rabbitmq-mqtt插件源码;
2.解压源码包:
unzip rabbitmq-mqtt-3.7.0
3.进入rabbitmq-mqtt-3.7.0/src目录
cd rabbitmq-mqtt-3.7.0/src
4.将rabbit_mqtt_processor.erl中qos1时持久化模式修改为非持久化:
5.进入rabbitmq-mqtt-3.7.0目录,开始编译程序:
make
6.将插件编译成.ez文件:
make dist
7.进入plugins目录,修改插件ez文件名称:
cd plugins/
mv rabbitmq_mqtt-0.0.0.ez rabbitmq_mqtt-3.7.0.ez
8.将rabbitmq安装后的插件存放目录/usr/lib/rabbitmq/lib/rabbitmq_server-3.7.0/plugins中的mqtt插件替换成重新生成的插件rabbitmq_mqtt-3.7.0.ez
9.如果之前有安装过mqtt插件和web_mqtt插件先使用rabbitmq-plugins disable plugin-name卸载mqtt插件和web_mqtt插件
10.重新安装mqtt插件和web_mqtt插件:
rabbitmq-plugins enable rabbitmq_mqtt
rabbitmq-plugins enable rabbitmq_web_mqtt
11.重新启动rabbitmq。
3、结果
重新开始测试,测试结果发现在同样的测试条件下,TPS轻松达到4w/s。