問題描述
有用戶提出通過udp協議發送數據到實時平臺,所以考慮在flume接收節點添加udp source來接收udp請求
- flume配置如下
1 2 3 4 |
a2.sources.syslog_udp_src.type=syslogudp a2.sources.syslog_udp_src.host=0.0.0.0 a2.sources.syslog_udp_src.port=5340 a2.sources.syslog_udp_src.channels=test_channel |
- 系統環境如下
1 2 3 |
CentOS Linux release 7.2.1511 4核cpu 8G內存 |
在測試中發現當發送速度超過500/s時開始出現數據丟失,開始以爲是網絡丟包,但通過tcpdump抓包比對發現並沒有丟包
netstat -su
命令顯示有數據包接收失敗
1 2 3 4 5 6 |
Udp: 4329216468 packets received 329761 packets to unknown port received. 301745872 packet receive errors 337569082 packets sent RcvbufErrors: 1061789 |
packets received
表示application接收到的數據包的數量packets to unknown port
表示由於端口沒有打開而丟失的數據包數據量,例如應用掛掉或者重啓packet receive errors
表示接收數據包的異常的數量,需要注意的是error和接收失敗的數據包並不是一對一的關係,即有一個數據包有可能會產生多個error
問題原因
當客戶端發送一個udp請求到flume時,整個流轉順序是這樣的
1 |
Client -> Kernel UDP socket buffer(4MB) -> Flume UDP Source buffer(64KB) -> Flume Channel buffer(磁盤,file channel) |
-
flume通過SyslogUDPSource接收UDP請求,底層是使用Netty的OioDatagramChannelFactory來創建服務器端channel,實現BIO(阻塞式IO)。BIO會對每個請求分配一個線程來處理,這種處理模式效率很低。經過測試,Flume UDP只能達到1100+的QPS,0.324MB/s
-
當client的發送速度大於flume udp source的接收速度時,數據開始在Kernel UDP socket buffer堆積,當buffer滿了的時候,後續的請求就會被丟棄,發生packet receive errors
-
即使平均吞吐量沒有超過flume udp的接收能力,但如果出現發送速度波動或者網絡波動導致短時間內Kernel UDP socket buffer被填滿,也會出現接收失敗
解決方案
- 網上看了一下,有的建議擴大Kernel UDP socket buffer,比如50MB
1 2
sysctl -w net.core.rmem_max=52428800 sysctl -w net.core.netdev_max_backlog=2000
但這只能在一定程度上避免波動的問題,如果發送速度大於接收速度,再大的緩存總會有滿的時候
-
用NIO重寫flume SyslogUDPSource,提高處理效率,目前並沒有這麼做的打算,因爲採取了下一個方案
-
部署syslog-ng接收udp請求,並轉發到flume tcp source。Syslog-ng是一個輕量框架,並不需要對數據進行過多的處理和緩存,可以直接對udp請求進行轉發,並且使用的是多路複用的NIO,因此處理效率很高。即使是這樣,經過壓力測試發現也同樣是有瓶頸,當qps大於24000時開始出現同樣的packet receive errors,因爲無論如何syslog-ng也是有處理上限的
1 2
2.5W QPS,丟包率0.008% 10W QPS,丟包率2.2%
-
以上兩種方案在數據量大到一定程度的時候都不可避免的丟包,所以如果可以的話,應該在數據發送之前就進行必要的過濾,把不需要的數據扔掉,降低請求數
-
最終的解決方案,也是我最推崇的:
放棄UDP,改用TCP