一、Netty高并發下資料丟失問題分析處理
1、現象
客戶端與服務端采用長連接通訊, 傳輸資料量不大情況下不會出現資料丟失,在大資料量高并發場景下,會出現極少數的資料丟失,存在偶發性, 仔細分析代碼處理邏輯,并無問題,查看netty的配置以及編解碼處理, 也沒有問題,
2、定位
資料丟失可能在傳輸任何環節都會出現, 需要定位縮小排查范圍, 在服務端整個流轉環節上都加上日志: 解碼器->資料接收->業務邏輯處理->資料發送->編碼器,發現資料接收的數量與客戶端發送的數量一致, 業務邏輯處理數與資料發送都一致,但編碼器的編碼數量不一致, 存在缺失,
3、分析
問題范圍已經明確,需要知道什么原因導致發送與編碼的數量不一致, 增加發送回呼處理,監聽失敗例外:

發現報出堆外記憶體超出限制的例外:

這里堆外記憶體的大小和JVM設定的Xmx大小一致, 都為16G,說明發送資料量過大, 產生嚴重的堆積阻塞,檢查代碼,因為是查詢hbase的歷史資料,回傳發送的資料量確實會很大,那么該如何處理? 很直接的方法,是直接增加記憶體, 但資料量如果再增長, 這種方式就很被動, 結合業務場景來看, 并不需要很強的實時性,那么可以在發送前做個檢查判斷, 如果水位線過高, 阻塞等待, 直到水位降低,再進行發送, 這樣就不會導致積壓過多訊息, 由于記憶體不足而產生資料丟失的問題, 這種方式會降低性能嗎? 如果網路沒有瓶頸且接收端處理速度足夠快,是不會產生影響,
4、解決
改造之前需要先了解Netty的高低水控制作用,ChannelOutboundBuffer 提供了高低水位線的配置,當buffer緩沖區大小超過高水位時,該Channel的isWritable狀態為false變成不可發送; 當buffer緩沖區低于低水位線時,isWritable又會變回true,可以重新發送資料, 那么高低水位線該如何配置? 在Netty服務端初始化的時候進行配置:

接下來改造發送代碼, 增加阻塞等待:

這樣改造貌似沒有問題, 但其實隱藏一個很大的坑, 如果客戶端例外斷開, 那么該channel會一直處于不可寫的狀態,占用服務端的處理執行緒,如果大量客戶端中斷,整個服務就會不可用,甚至造成崩潰的風險, 進一步改造:

增加channel的有效性判斷,同時設定最大等待上限, 每次阻塞100毫秒, 總計30秒的等待時間,實際根據業務場景進行設定, 對于我們業務來講,時間是足夠,至此,服務端發送資料丟失的問題, 就已經成功解決,
二、Netty服務端性能優化配置
1、場景
適用大資料量,請求互動頻繁的場景, 采用自定義的二進制資料結構進行傳輸,
2、服務端配置
1) 基礎配置

bossGroup與workGroup如果沒有特殊需要, 可以按默認配置, TCP/IP 第三次握手的請求佇列最大長度, 如果在TCP連接上沒有丟包與阻塞, 不需要設定過大,
2) 高低水位與緩沖區設定

此配置可以支撐5~10M/S的資料處理, 如有更大資料量,可以適當調高些,但不能設定過大,
3)編解碼等其他配置

為防止粘包/拆包問題,采用LengthFieldBasedFrameDecoder解碼器, 偏移量根據自定義二進制資料結構進行設定,
3、編碼與解碼配置
1)編碼配置
通過MessageToByteEncoder進行編碼,為減少記憶體空間占用, 及時釋放回收,可以將相關參考都設為null,

2) 解碼配置
通過ByteToMessageDecoder進行解碼, 最后呼叫discardReadBytes()方法,及時釋放空間,減少記憶體占用,

4、業務處理異步化
Netty雖然有較高的性能, 但業務邏輯如果單執行緒處理, 仍可能出現阻塞,這時候開啟采用執行緒池做業務邏輯處理, 保障性能最大化,
1)執行緒池定義:

2)執行緒池拒絕handler處理
這里是直接拒絕, 如果不想回傳失敗, 也阻塞, 等待重新加入執行緒池處理,

3) 在handle進行呼叫處理

業務邏輯異步處理完成之后,再通過DataHander中傳入的ctxl通道背景關系進行資料發送,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/423245.html
標籤:其他
上一篇:ES報錯Native controller process has stopped - no new native processes can be started
下一篇:大資料生態
