内容简介
闪电侠(俞超),某互联网公司技术专家。精通Netty、Spring、MyBatis 等开源框架。负责公司各类长连项目的开发与维护,有千万级别实时在线连接、百亿吞吐长连通信经验。Vim和IDEA党,对效率和编写优美代码有极致追求。
简书“Netty源码分析”专栏作者。
异常火爆的慕课“IntelliJ IDEA神器使用技巧”作者。
慕课“Netty源码分析视频教程”作者。
微信公众号:闪电侠的博客。
个人微信号: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
· · · · · ·
作者简介
闪电侠(俞超),某互联网公司技术专家。精通Netty、Spring、MyBatis 等开源框架。负责公司各类长连项目的开发与维护,有千万级别实时在线连接、百亿吞吐长连通信经验。Vim和IDEA党,对效率和编写优美代码有极致追求。
简书“Netty源码分析”专栏作者。
异常火爆的慕课“IntelliJ IDEA神器使用技巧”作者。
慕课“Netty源码分析视频教程”作者。
微信公众号:闪电侠的博客。
个人微信号:823797837。
欢迎读者联系笔者进行技术交流。