這是一本專門為Netty初學者打造的入門及進階學習圖書,無論你之前有沒有使用過Netty,都可以從本書中有所收獲。 本書分上下兩篇。上篇通過一個即時聊天系統的實戰案例,讓讀者能夠系統地使用一遍Netty,全面掌握Netty的知識點;下篇通過對源碼的層層剖析,讓讀者能夠掌握Netty底層原理,知其然并知其所以然,從而編寫出高性能網絡應用程序。 如果你想全面系統地學習Netty,并掌握一些性能調優方法,本書上篇可以幫助你完成這個目標。如果你想深入了解Netty的底層設計,編寫出更靈活高效的網絡通信程序,本書下篇可以幫助你完成這個目標。如果你從未讀過開源框架源碼,本書將是你的第一本源碼指導書,讀源碼并不難,難的是邁出這一小步,之后就能通往更廣闊的世界。
閃電俠(俞超),某互聯網公司技術專家。精通Netty、Spring、MyBatis,熟讀互聯網公司常見開發框架源碼。負責公司各類長連項目的開發與維護,有千萬級別實時在線連接、百億吞吐長連通信經驗。Vim黨,IDEA黨,對效率和編寫優美代碼有極致的追求。?簡書“Netty源碼分析”專欄作者。?異常火爆的“IntelliJ IDEA神器使用技巧”作者。?“Netty源碼分析視頻教程”作者。?Github:lightningMan。?微信公眾號“閃電俠的博客”專欄作者。?個人微信號:823797837。
目 錄
上篇 入門實戰
第1章 即時聊天系統簡介 2
1.1 單聊流程 2
1.2 單聊的指令 3
1.2.1 指令圖示 3
1.2.2 指令列表 4
1.3 群聊流程 4
1.4 群聊要實現的指令集 5
1.4.1 指令圖示 5
1.4.2 指令列表 6
1.5 Netty 7
1.5.1 客戶端使用Netty的程序邏輯結構 7
1.5.2 服務端使用Netty的程序邏輯結構 8
1.6 本書實現的即時聊天形式 8
第2章 Netty是什么 9
2.1 IO編程 9
2.2 NIO編程 12
2.2.1 線程資源受限 12
2.2.2 線程切換效率低下 13
2.2.3 IO讀寫面向流 13
2.3 Netty編程 16
第3章 Netty開發環境配置 20
3.1 Maven 20
3.1.1 下載 20
3.1.2 配置和驗證 21
3.2 Git 21
3.2.1 下載與安裝 21
3.2.2 配置 22
3.3 IntelliJ IDEA 22
第4章 服務端啟動流程 27
4.1 服務端啟動最小化代碼 27
4.2 自動綁定遞增端口 28
4.3 服務端啟動的其他方法 30
4.3.1 handler()方法 30
4.3.2 attr()方法 30
4.3.3 childAttr()方法 30
4.3.4 option()方法 31
4.3.5 childOption()方法 31
4.4 總結 31
第5章 客戶端啟動流程 32
5.1 客戶端啟動Demo 32
5.2 失敗重連 34
5.3 客戶端啟動的其他方法 35
5.3.1 attr()方法 35
5.3.2 option()方法 36
5.4 總結 36
5.5 思考 36
第6章 客戶端與服務端雙向通信 37
6.1 客戶端發送數據到服務端 37
6.2 服務端讀取客戶端數據 39
6.3 服務端返回數據到客戶端 40
6.4 總結 42
6.5 思考 42
第7章 數據載體ByteBuf的介紹 43
7.1 ByteBuf的結構 43
7.2 容量API 44
7.3 讀寫指針相關的API 45
7.4 讀寫API 46
7.5 實戰 49
7.6 總結 52
7.7 思考 52
第8章 客戶端與服務端通信協議編解碼 53
8.1 什么是客戶端與服務端的通信協議 53
8.2 通信協議的設計 54
8.3 通信協議的實現 56
8.3.1 Java對象 56
8.3.2 序列化 57
8.3.3 編碼:封裝成二進制數據的過程 59
8.3.4 解碼:解析Java對象的過程 59
8.4 總結 61
8.5 思考 61
第9章 實現客戶端登錄 62
9.1 登錄流程 62
9.2 邏輯處理器 63
9.3 客戶端發送登錄請求 64
9.3.1 客戶端處理登錄請求 64
9.3.2 服務端處理登錄請求 64
9.4 服務端發送登錄響應 65
9.4.1 服務端處理登錄響應 65
9.4.2 客戶端處理登錄響應 66
9.5 總結 67
9.6 思考 67
第10章 實現客戶端與服務端收發消息 68
10.1 收發消息對象 68
10.2 判斷客戶端是否登錄成功 69
10.3 在控制臺輸入消息并發送 71
10.4 服務端收發消息處理 72
10.5 客戶端收消息處理 72
10.6 總結 74
10.7 思考 74
第11章 Pipeline與ChannelHandler 75
11.1 Pipeline與ChannelHandler的構成 76
11.2 ChannelHandler的分類 76
11.3 ChannelInboundHandler的事件傳播 77
11.4 ChannelOutboundHandler的事件傳播 79
11.5 總結 81
11.6 思考 82
第12章 構建客戶端與服務端的Pipeline 83
12.1 ChannelInboundHandlerAdapter與ChannelOutboundHandlerAdapter 83
12.2 ByteToMessageDecoder 85
12.3 SimpleChannelInboundHandler 85
12.4 MessageToByteEncoder 87
12.5 構建客戶端與服務端的Pipeline 89
12.6 總結 90
12.7 思考 91
第13章 拆包/粘包理論與解決方案 92
13.1 拆包/粘包例子 92
13.2 為什么會有粘包、半包現象 94
13.3 拆包的原理 95
13.4 Netty自帶的拆包器 95
13.4.1 固定長度的拆包器FixedLengthFrameDecoder 95
13.4.2 行拆包器LineBasedFrameDecoder 95
13.4.3 分隔符拆包器DelimiterBasedFrameDecoder 95
13.4.4 基于長度域的拆包器LengthFieldBasedFrameDecoder 96
13.5 如何使用LengthFieldBasedFrameDecoder 96
13.6 拒絕非本協議連接 97
13.7 客戶端和服務端的Pipeline結構 99
13.8 總結 99
13.9 思考 99
第14章 ChannelHandler的生命周期 100
14.1 ChannelHandler的生命周期詳解 100
14.2 ChannelHandler生命周期各回調方法的用法舉例 104
14.2.1 ChannelInitializer的實現原理 104
14.2.2 handlerAdded()方法與handlerRemoved()方法 106
14.2.3 channelActive()方法與channelInActive()方法 106
14.2.4 channelRead()方法 106
14.2.5 channelReadComplete()方法 106
14.3 總結 106
14.4 思考 107
第15章 使用ChannelHandler的熱插拔實現客戶端身份校驗 108
15.1 身份檢驗 108
15.2 移除校驗邏輯 110
15.3 身份校驗演示 111
15.3.1 有身份認證的演示 112
15.3.2 無身份認證的演示 113
15.4 總結 114
15.5 思考 114
第16章 客戶端互聊的原理與實現 115
16.1 最終效果 115
16.2 一對一單聊的原理 116
16.3 一對一單聊的實現 117
16.3.1 用戶登錄狀態與Channel的綁定 117
16.3.2 服務端接收消息并轉發的實現 119
16.3.3 客戶端接收消息的邏輯處理 121
16.3.4 客戶端控制臺登錄和發送消息 121
16.4 總結 122
16.5 思考 123
第17章 群聊的發起與通知 124
17.1 最終效果 124
17.2 群聊的原理 125
17.3 控制臺程序重構 127
17.3.1 創建控制臺命令執行器 127
17.3.2 管理控制臺命令執行器 127
17.4 創建群聊的實現 129
17.4.1 客戶端發送創建群聊請求 129
17.4.2 服務端處理創建群聊請求 129
17.4.3 客戶端處理創建群聊響應 131
17.5 總結 132
17.6 思考 132
第18章 群聊的成員管理 133
18.1 最終效果 133
18.2 群的加入 135
18.2.1 在控制臺添加群加入命令處理器 135
18.2.2 服務端處理加群請求 136
18.2.3 客戶端處理加群響應 137
18.3 群的退出 137
18.4 獲取群成員列表 138
18.4.1 在控制臺添加獲取群成員列表命令處理器 138
18.4.2 服務端處理獲取群成員列表請求 139
18.4.3 客戶端處理獲取群成員列表響應 140
18.5 總結 141
18.6 思考 142
第19章 群聊消息的收發及Netty性能優化 143
19.1 群聊消息的最終效果 143
19.2 群聊消息的收發實現 145
19.3 共享Handler 146
19.4 壓縮Handler——合并編解碼器 147
19.5 縮短事件傳播路徑 149
19.5.1 壓縮Handler——合并平行Handler 149
19.5.2 更改事件傳播源 151
19.6 減少阻塞主線程的操作 152
19.7 如何準確統計處理時長 153
19.8 總結 154
第20章 心跳與空閑檢測 156
20.1 網絡問題 156
20.2 服務端空閑檢測 158
20.3 客戶端定時發心跳數據包 159
20.4 服務端回復心跳與客戶端空閑檢測 160
20.5 總結 162
20.6 思考 162
下篇 源碼分析
第21章 服務端啟動流程解析 164
21.1 服務端啟動示例 164
21.2 服務端啟動的核心步驟 166
21.3 創建服務端Channel 168
21.3.1 創建JDK底層Channel 170
21.3.2 創建 Channel 配置類 171
21.3.3 設置Channel類型為非阻塞 171
21.3.4 創建Channel核心組件 172
21.3.5 創建服務端Channel小結 174
21.4 初始化服務端Channel 174
21.4.1 設置服務端Channel的Option與Attr 174
21.4.2 設置客戶端Channel的Option與Attr 175
21.4.3 配置服務端啟動邏輯 175
21.4.4 初始化服務端Channel小結 176
21.5 注冊服務端Channel 176
21.5.1 調用JDK底層注冊Selector 177
21.5.2 回調handlerAdded事件 178
21.5.3 傳播channelRegistered事件 178
21.5.4 其他邏輯 178
21.5.5 注冊服務端Channel小結 179
21.6 綁定服務端端口 180
21.6.1 調用JDK底層綁定端口 181
21.6.2 傳播channelActive事件 182
21.6.3 注冊ACCEPT事件 182
21.6.4 綁定服務端端口小結 183
21.7 總結 184
第22章 Reactor線程模型解析 185
22.1 NioEventLoopGroup的創建 185
22.1.1 確定NioEventLoop的個數 186
22.1.2 NioEventLoopGroup的創建總體框架 187
22.1.3 創建ThreadPerTaskExecutor 188
22.1.4 創建NioEventLoop 190
22.1.5 創建線程選擇器 192
22.1.6 NioEventLoopGroup的創建小結 194
22.2 NioEventLoop對應線程的創建和啟動 195
22.2.1 NioEventLoop的啟動入口 195
22.2.2 創建線程并啟動 195
22.3 NioEventLoop的執行流程 198
22.3.1 NioEventLoop的執行總體框架 198
22.3.2 執行一次事件輪詢 199
22.3.3 處理產生 IO 事件的 Channel 205
22.3.4 添加任務 213
22.3.5 任務的執行 220
22.3.6 NioEventLoop 的執行流程小結 225
22.4 總結 225
第23章 客戶端連接接入流程解析 227
23.1 新連接接入的總體流程 228
23.2 檢測到有新連接 228
23.3 注冊Reactor線程 229
23.3.1 創建NioSocketChannel 230
23.3.2 設置并綁定NioSocketChannel 233
23.3.3 注冊 Reactor 線程小結 240
23.4 總結 240
第24章 編碼原理解析 241
24.1 粘包與拆包 241
24.1.1 為什么要粘包 241
24.1.2 為什么要拆包 242
24.2 拆包的原理 242
24.3 Netty中拆包的基類 242
24.4 拆包抽象 243
24.4.1 累加數據 244
24.4.2 將累加的數據傳遞給業務進行拆包 245
24.4.3 清理字節容器 246
24.4.4 將業務數據包傳遞給業務解碼器處理 247
24.5 行拆包器 248
24.5.1 找到換行符的位置 248
24.5.2 非discarding模式的處理 249
24.5.3 discarding模式的處理 250
24.6 特定分隔符拆包 251
24.7 LengthFieldBasedFrameDecoder 進階用法 251
24.7.1 基于長度的拆包 251
24.7.2 基于長度的截斷拆包 252
24.7.3 基于偏移長度的拆包 252
24.7.4 基于可調整長度的拆包 253
24.7.5 基于偏移可調整長度的截斷拆包 253
24.7.6 基于偏移可調整變異長度的截斷拆包 254
24.8 LengthFieldBasedFrameDecoder源碼剖析 254
24.8.1 構造函數 254
24.8.2 實現拆包抽象 255
24.9 總結 260
第25章 ChannelPipeline解析 261
25.1 ChannelPipeline的初始化 262
25.2 ChannelPipeline添加ChannelHandler 263
25.2.1 檢查是否有重復的Handler 265
25.2.2 創建節點 266
25.2.3 添加節點 271
25.2.4 回調用戶方法 272
25.2.5 ChannelPipeline添加ChannelHandler小結 274
25.3 ChannelPipeline刪除ChannelHandler 274
25.3.1 找到待刪除的節點 275
25.3.2 調整雙向鏈表指針并刪除 276
25.3.3 回調用戶方法 277
25.3.4 ChannelPipeline刪除ChannelHandler小結 278
25.4 Inbound事件的傳播 278
25.4.1 Unsafe是什么 278
25.4.2 ChannelPipeline中的HeadContext 282
25.4.3 ChannelPipeline中的Inbound事件傳播 289
25.4.4 ChannelPipeline中的TailContext 291
25.4.5 Inbound事件的傳播小結 294
25.5 Outbound事件的傳播 294
25.6 ChannelPipeline中異常的傳播 299
25.6.1 Inbound異常的傳播 299
25.6.2 Outbound異常的傳播 301
25.7 總結 302
第26章 writeAndFlush解析 304
26.1 Pipeline中的標準鏈表結構 304
26.2 Java對象編碼過程 305
26.3 write:寫隊列 308
26.4 flush:刷新寫隊列 311
26.4.1 獲得第一個需要flush的節點的數據 313
26.4.2 獲得自旋鎖的迭代次數 314
26.4.3 采用自旋方式將ByteBuf寫出JDK NIO的Channel 314
26.4.4 刪除該節點 315
26.5 writeAndFlush:寫隊列并刷新 316
26.6 總結 317
第27章 本書總結 319
27.1 Netty是什么 319
27.2 服務端和客戶端的啟動 319
27.3 ByteBuf 320
27.4 自定義協議拆包與編解碼 320
27.5 Handler與Pipeline 320
27.6 耗時操作的處理與統計 321
27.7 最后的話 321