閃電俠 Netty 小冊裡的騷操作

weixin_34194087發表於2018-10-13

前言

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

放個小冊地址:Netty 入門與實戰:仿寫微信 IM 即時通訊系統

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

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

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

開始

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

  1. 服務端啟動流程
1. 通過給 bind 方法新增監聽器,用以自動繫結遞增埠。算騷操作吧?
2. attr 方法,為每條連線增加屬性,能夠實現全單例模式喲
3. childOption 方法,關於 TCP連線的優化,SO_KEEPALIVE 底層心跳,TCP_NODELAY 延遲傳送,SO_BACKLOG 等待佇列
  1. 客戶端啟動流程
1. 還是通過監聽器實現重試,但是是 connect 返回的 future,且重實間隔時間左移 1 位增加(效能優化,不使用乘二 ,優秀)。
2. 重試不在主執行緒,而是使用 bootstrap.config().group().schedule 搞定時任務,和我想的不一樣。優秀
3. 客戶端需要 CONNECT_TIMEOUT_MILLIS 屬性
  1. 客戶端與服務端雙向通訊
1.客戶端在 channelActive 立刻搞事情,嗯,rpc 通訊通常也會做一些處理,例如列印客戶端ip之類的。
  1. 客戶端與服務端通訊協議編解碼(擴充套件較多)
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 校驗碼(金融場景必備)
  1. 實現客戶端與服務端收發訊息
1. 使用 hannel.attr(Attributes.LOGIN).set(true) 繫結登入標識方便喲
  1. 構建客戶端與服務端 pipeline
1. 常用的 ChannelInboundHandlerAdapter 和 ChannelOutboundHandlerAdapter 但是需要強轉哦,麻煩,
   建議使用 SimpleChannelInboundHandler (還幫你釋放記憶體喲)。
2. 不使用 MessageToByteEncoder ,可以自己編解碼哦,雖然麻煩點
  1. 拆包粘包理論與解決方案
1. 常用拆包:固定長度,行拆包(有bug,我寫過文章分析),分隔符,基於長度
2. 最通用的就是基於長度,只要你的自定義協議中包含長度域欄位,就可以使用
3. LengthFieldBasedFrameDecoder 代替自己繼承 ByteToMessageDecoder 喲。
4. 對 LengthFieldBasedFrameDecoder 擴充套件一下,校驗魔數關閉錯誤連線美滋滋。
  1. channelHandler 的生命週期
1. 在 channelReadComplete 方法裡執行 flush,批量重新整理,效能提升美滋滋。
2. channelActive 和 channelInActive 增減連線,RPC 都這麼幹
  1. 使用 channelHandler 的熱插拔實現客戶端身份校驗
1. ctx.pipeline().remove(this) 刪除沒有必要的 handler 美滋滋
2. handlerRemoved 回撥美滋滋
  1. 客戶端互聊原理與實現
1. Session 通過 channel.attr(Attributes.SESSION).set(session) 繫結連線美滋滋。
2. channel.attr(Attributes.SESSION).set(null) 刪除 session 美滋滋
3. channel.attr(Attributes.SESSION).get() 美滋滋
  1. 群聊的發起與通知
1. ChannelGroup c = ChannelGroup channelGroup = new DefaultChannelGroup(ctx.executor()) 批量處理連線美滋滋
2. channelGroup.writeAndFlush 批量寫連線美滋滋

高能預警!!!!

  1. 牛逼的效能優化
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!!!

微信:

4236553-61c78440ada12546.png

相關文章