Netty之UDP丟包解決

程序背景

程序是Java編寫,基於Netty框架寫的客戶端及服務端。html

現象

客戶端大數據量持續發UDP數據,做爲UDP服務器出現了部分數據頻繁丟失觸發程序自身重傳邏輯。
經過GC日誌對比發現丟包的時間點偶有處於Full GC,說明Java程序接收間歇性stop world的不是根因。java

觀察Udp的dump

經過watch -n 1 -d 'cat /proc/net/udp >> /usr/udpDump.txt'在發送數據的過程當中持續觀察Udp緩衝區的情況linux

  • /proc/net/udp是瞬時的Udp socket dump,另有/proc/net/udp6用於監控IPv6
  • dump輸出裏的tx_queue是發送緩衝區,rx_queue是接收緩衝區,單位都是byte
  • 若是應用層收發效率足夠好,正常狀況下tx_queuerx_queue二者永遠是0
  • 發送數據過程當中頻現rx_queue>0,說明Udp緩衝區有堆積現象
  • 輸出解釋見How to monitor Linux UDP buffer available space?Meaning of fields in /proc/net/udp

觀察Udp的stats

經過watch -n 1 -d 'netstat -su >> /usr/udpStats.txt'持續觀察Udp的stats輸出git

  • 輸出裏packets received的值指應用層從讀入緩衝區裏取走的包
  • 輸出裏packets to unknown port received的值指端口無應用監聽而分發至該端口的包
  • 輸出裏packet receive errors的值指Udp接收錯誤數,正常狀況下應該是0,在觀察中不停增長,證實出現Udp包溢出接收緩衝區的狀況
    • 發生錯誤的包數與接收錯誤數非一一對應
  • 資料參見Udp Packet Receive ErrorsUdp packet drops and packet receive error difference

解決問題

服務端代碼優化

定論:
默認的UDP socket讀緩衝區不夠引起系統丟棄UDP包。
服務端代碼優化設置UDP socket讀緩衝區爲2M,代碼以下github

Bootstrap selfBootStrap = new Bootstrap();
selfBootStrap.group(group);
selfBootStrap.channel(NioDatagramChannel.class);
selfBootStrap.option(ChannelOption.SO_BROADCAST, true);
// 這一行設置了UDP socket讀緩衝區爲2M
selfBootStrap.option(ChannelOption.SO_RCVBUF, 1024 * 2048);
selfBootStrap.handler(channelInitializer);
selfBootStrap.localAddress(selfPort);

理論上Udp socket讀緩衝區設置爲2M在咱們的測試場景下已經足夠。優化後雖有改善但仍有丟包現象。bash

Linux系統級調優

定論:
應用層設置了UDP socket緩衝區不必定在Linux上生效,緣由在於Linux對Udp socket緩衝區存有系統級限制,超過該限制的緩衝區大小無效。服務器

Windows對socket的緩衝區沒有限制框架

要點分析:
Linux經過net.core.rmem_max控制Udp的讀緩衝區,經過net.core.wmem_max控制Udp的寫緩衝區。
在程序的啓動sh腳本里添加以下代碼修改net.core.rmem_maxsocket

# 服務器默認UDP讀緩衝區最大128K。修改成2G。解決UDP丟包問題
rmemCount=`cat /etc/sysctl.conf|grep "net.core.rmem_max" | wc -l`
if [ ${rmemCount} -eq 0 ]
then
    echo "net.core.rmem_max = 2147483647" >> /etc/sysctl.conf
    sysctl -p
fi

腳本的做用就是修改/etc/sysctl.conf文件,並鍵入sysctl -p命令使自定義參數生效。
資料參見Improving UDP Performance by Configuring OS UDP Buffer LimitsUDP Drops on Linuxide

相關文章
相關標籤/搜索