閃電俠 Netty 小冊里的騷操作


前言

即使這是一本小冊,但基於“不提筆不讀書”的理念,仍然有必要總結一下。此小冊對於那些“硬杠 Netty 源碼 卻不曾在千萬級生產環境上使用實操”的用戶非常有用。當然,對那些沒有 Netty 編程經驗的人來說,更為有用。

放個小冊地址:[Netty 入門與實戰:仿寫微信 IM 即時通訊系統
](https://juejin.im/book/5b4bc28bf265da0f60130116/section/5b4db06d5188251afc257383#heading-2)

再次強烈推薦,一碗黃燜雞/半杯 Luckin coffee/一包炫赫門 的價錢,可以讓你學會使用 Java 界的 epoll 進行多路復用網絡編程,不能說是不划算的 :)

本文標題含有“騷操作”,為什么這么說呢?

作者是某團某評基礎架構部技術專家,長期負責后台千萬級別的推送系統,而這些推送系統自然是長連接實現的。可以想象,作者的這些實踐經驗不可謂不好用,縱然看過源碼,提過 issue,本人也覺得這些操作非常好用,非常騷氣。

開始

我們挑重點講,雖然對於強迫症來講,每一節都有筆記才是最吼的!

1 服務端啟動流程

1. 通過給 bind 方法添加監聽器,用以自動綁定遞增端口。算騷操作吧?
2. attr 方法,為每條連接增加屬性,能夠實現全單例模式喲
3. childOption 方法,關於 TCP連接的優化,SO_KEEPALIVE 底層心跳,TCP_NODELAY 延遲發送,SO_BACKLOG 等待隊列

2 客戶端啟動流程

1. 還是通過監聽器實現重試,但是是 connect 返回的 future,且重實間隔時間左移 1 位增加(性能優化,不使用乘二 ,優秀)。
2. 重試不在主線程,而是使用 bootstrap.config().group().schedule 搞定時任務,和我想的不一樣。優秀
3. 客戶端需要 CONNECT_TIMEOUT_MILLIS 屬性

3 客戶端與服務端雙向通信

1.客戶端在 channelActive 立刻搞事情,嗯,rpc 通信通常也會做一些處理,例如打印客戶端ip之類的。

4 客戶端與服務端通信協議編解碼(擴展較多)

emm,這個其實就是自定義應用層協議。
1. 4 字節魔數校驗,例如 dubbo 就使用0xdabb進行校驗,Java 字節碼也使用 0xcafebabe 校驗字節碼。
2.  版本號肯定需要的
3. 序列化算法,肯定也需要的
4. 指令,肯定也是需要的,不過,也可以使用別的方式。
5. 后面的數據長度,也是需要的,方便拆包。

其實這里可以參照 RPC 協議來看,這里更像一個簡化的 RPC 協議。
一般 RPC 框架首先獲取協議類型,根據這個協議類型得到協議處理器,然后再處理(一個端口處理多個協議的場景)。
生產級別的 RPC 通常較為復雜,以 SOFABolt 為例,需要以下字段:
1. 協議版本
2. 請求類型,即指令(Request,Response, oneway)
3. 指令版本
4. RequestID 負責數據對應
5. 序列號器
6. 協議開關(例如 CRC 校驗,安全校驗)
7. 響應碼,約定異常,簡化異常
8. 類名長度,Java rpc 框架必備
9. 請求頭長度(參照 http header)
10. 請求體長度(參照http body)
11. 類名
12. 業務請求頭內容(一般是 Map,SOFABolt 支持自定義,SOFARPC 里面藏着是否泛化調用等信息)
13. 業務請求體內容(一般就是個Request對象或 Response對象,里面包含約定的屬性,例如參數,返回值,超級多,SOFARPC 有個屬性類 RemotingConstants,這里都有)
14. CRC 校驗碼(金融場景必備)

5 實現客戶端與服務端收發消息

1. 使用 hannel.attr(Attributes.LOGIN).set(true) 綁定登錄標識方便喲

6 構建客戶端與服務端 pipeline

1. 常用的 ChannelInboundHandlerAdapter 和 ChannelOutboundHandlerAdapter 但是需要強轉哦,麻煩,
   建議使用 SimpleChannelInboundHandler (還幫你釋放內存喲)。
2. 不使用 MessageToByteEncoder ,可以自己編解碼哦,雖然麻煩點

7 拆包粘包理論與解決方案

1. 常用拆包:固定長度,行拆包(有bug,我寫過文章分析),分隔符,基於長度
2. 最通用的就是基於長度,只要你的自定義協議中包含長度域字段,就可以使用
3. LengthFieldBasedFrameDecoder 代替自己繼承 ByteToMessageDecoder 喲。
4. 對 LengthFieldBasedFrameDecoder 擴展一下,校驗魔數關閉錯誤連接美滋滋。

8 channelHandler 的生命周期

1. 在 channelReadComplete 方法里執行 flush,批量刷新,性能提升美滋滋。
2. channelActive 和 channelInActive 增減連接,RPC 都這么干

9 使用 channelHandler 的熱插拔實現客戶端身份校驗

1. ctx.pipeline().remove(this) 刪除沒有必要的 handler 美滋滋
2. handlerRemoved 回調美滋滋

10 客戶端互聊原理與實現

1. Session 通過 channel.attr(Attributes.SESSION).set(session) 綁定連接美滋滋。
2. channel.attr(Attributes.SESSION).set(null) 刪除 session 美滋滋
3. channel.attr(Attributes.SESSION).get() 美滋滋

11 群聊的發起與通知

1. ChannelGroup c = ChannelGroup channelGroup = new DefaultChannelGroup(ctx.executor()) 批量處理連接美滋滋
2. channelGroup.writeAndFlush 批量寫連接美滋滋

高能預警!!!!

12 牛逼的性能優化

1. 共享 handler
2. 壓縮 handler - 合並編解碼器 —— MessageToMessageCodec
3. 雖然有狀態的 handler 不能搞單例,但是你可以綁定到 channel 屬性上,強行單例
4. 縮短事件傳播路徑—— 放 Map 里,在第一個 handler 里根據指令來找具體 handler。
5. 更改事件傳播源—— 用 ctx.writeAndFlush() 不要用 ctx.channel().writeAndFlush()
6. 減少阻塞主線程的操作—— 使用業務線程池,RPC 優化重點
7. 計算耗時,使用回調 Future

13 心跳和空閑檢測

1. 空閑檢測 IdleStateHandler 用起來很爽, channelIdle 和 userEventTriggered 都可以處理
2. 定時心跳 ctx.executor().scheduleAtFixedRate 很優秀
3. 通常空閑檢測時間要比發送心跳的時間的兩倍要長一些(3倍),這也是為了排除偶發的公網抖動,防止誤判。美滋滋

總結

小小短文,無法盡數 Netty 精華,但對於新手來說,已經足夠使用了。而我這里僅僅是做簡單的閱讀總結,更多的內容,還需要讀者自己去研究小冊,研究源碼,研究 Netty 在 RPC 里的運用,方能成為 Netty 多路復用網絡編程高手。

關於 RPC 里使用 Netty 的最佳范例,推薦螞蟻金服開源框架 SOFABolt,可以說是對 Netty 編程最佳實踐的提煉,和此文相輔相成進行學習,可助汝縱橫 Java 各種面試。

good luck!!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM