現象
Mqtt Consumer應該收到的消息少於預期,登錄ActiveMQ的管理頁面里的Topics,查看Messages Enqueued發現同樣少於理應接收的數量。
定位問題
- 懷疑是TCP丟包,通過
netstat -s命令觀察發送消息前后Tcp信息的輸出 - 對比兩次Tcp信息的輸出,發現packets pruned from receive queue because of socket buffer overrun與packets collapsed in receive queue due to low socket buffer等含有pruned或collapsed字樣的數值在增多。
- collapsed是指tcp包溢出緩沖區,此時內核嘗試通過減少內存開銷以換取接收隊列里的空閑空間,策略是CPU換內存
- pruned是指內核在collapsed時的嘗試后,仍未有足夠空間接收包則此時直接扔包
- 解釋來自於Red Hat Enterprise Linux Network Performance Tuning Guide 頁碼22
解決方案
- 首先調整系統級tcp的緩沖區,修改/etc/sysctl.conf如下
net.core.rmem_max = 8388608
net.core.wmem_max = 8388608
net.core.rmem_default = 655360
net.core.wmem_default = 655360
net.ipv4.tcp_rmem = 4096 655360 8388608 # Tcp接收緩沖區,分別是最小、默認、最大
net.ipv4.tcp_wmem = 4096 655360 8388608 # Tcp發送緩沖區,分別是最小、默認、最大
net.ipv4.tcp_mem = 8388608 8388608 8388608
- 上述參數的解釋參見How To: Network / TCP / UDP Tuning、Red Hat Enterprise Linux Network Performance Tuning
Guide、Linux Kernel Tuning - linux終端里輸入
sysctl -p使之生效 - 接着修改ActiveMQHome/conf/activemq.xml如下
<transportConnector name="mqtt"
uri="mqtt+nio://0.0.0.0:1883?maximumConnections=1000&
wireFormat.maxFrameSize=104857600&transport.ioBufferSize=1048576&
transport.socketBufferSize=4194304"/>
- 其中**+nio**表示啟用**nio**方式的socket通信。Java里**nio**方式的socket比**bio**方式的更高效。mqtt默認采用**bio**。
- **socketBufferSize**調整緩沖區大小為4m,默認為64k,防止socket接收緩沖過小引發系統扔包
- **ioBufferSize**調整程序內部使用的緩沖區大小為1m,默認為8k,提高緩沖可以增加處理性能
代碼分析
MQTTTransportFactory繼承自TcpTransportFactoryorg.apache.activemq.transport.tcp.TcpTransportFactory#doBind時解析URI帶入的參數
org.apache.activemq.transport.mqtt.MQTTNIOTransportFactory#createTcpTransportServer創建TcpTransportServerorg.apache.activemq.transport.tcp.TcpTransportServer#doRunWithServerSocketChannel創建與客戶端通信的Transport- 默認的
socketBufferSize = 65536 - 默認的
ioBufferSize = 8192
- 默認的
Transport受org.apache.activemq.transport.TransportAcceptListener#onAccept處理Transport被扔給org.apache.activemq.thread.TaskRunnerFactory線程池- 在線程池中創建
org.apache.activemq.broker.Connection
Connection中的org.apache.activemq.broker.TransportConnection#start啟動整個TCP的鏈路
Windows下定位問題的要點
- windows下的
netstat -e -s等價於linux下的netstat -s - windows的socket緩沖區沒有系統級限制,應用程序可以按需調整,資料來源於What is the size of a socket send buffer in Windows?、Design issues - Sending small data segments over TCP with Winsock
