Skip to content

Commit 224a671

Browse files
committed
新增文章'Netty概念扫盲'
1 parent 27bd0a6 commit 224a671

2 files changed

Lines changed: 243 additions & 14 deletions

File tree

README.md

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -113,22 +113,9 @@ Java相关流行框架源码分析,学习以及总结,项目持续更新中
113113
- [深入浅出SpringSecurity和OAuth2(三)—— WebSecurity建造核心逻辑](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/SpringSecurity/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%B3%BB%E7%BB%9F%E5%AD%A6%E4%B9%A0SpringSecurity%E5%92%8COAuth2%EF%BC%88%E4%B8%89%EF%BC%89%E2%80%94%E2%80%94%20WebSecurity%E5%BB%BA%E9%80%A0%E6%A0%B8%E5%BF%83%E9%80%BB%E8%BE%91.md)
114114
- [深入浅出SpringSecurity和OAuth2(四)—— FilterChainProxy过滤器链中的几个重要的过滤器](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/SpringSecurity/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%B3%BB%E7%BB%9F%E5%AD%A6%E4%B9%A0SpringSecurity%E5%92%8COAuth2%EF%BC%88%E5%9B%9B%EF%BC%89%E2%80%94%E2%80%94%20FilterChainProxy%E8%BF%87%E6%BB%A4%E5%99%A8%E9%93%BE%E4%B8%AD%E7%9A%84%E5%87%A0%E4%B8%AA%E9%87%8D%E8%A6%81%E7%9A%84%E8%BF%87%E6%BB%A4%E5%99%A8.md)
115115

116-
- Dubbo底层源码解析
117-
- Dubbo底层源码版本:2.7.8
118-
- [Dubbo底层源码学习—— 源码搭建](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Dubbo/Dubbo%E6%BA%90%E7%A0%81%E6%90%AD%E5%BB%BA.md)
119-
- [Dubbo底层源码学习(一)—— Dubbo的URL](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Dubbo/Dubbo%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%EF%BC%88%E4%B8%80%EF%BC%89%E2%80%94%E2%80%94%20Dubbo%E7%9A%84URL.md)
120-
- [Dubbo底层源码学习(二)—— Dubbo的SPI机制(上)](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Dubbo/Dubbo%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%EF%BC%88%E4%BA%8C%EF%BC%89%E2%80%94%E2%80%94%20Dubbo%E7%9A%84SPI%E6%9C%BA%E5%88%B6%EF%BC%88%E4%B8%8A%EF%BC%89.md)
121-
- [Dubbo底层源码学习(二)—— Dubbo的SPI机制(中)](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Dubbo/Dubbo底层源码学习%EF%BC%88二%EF%BC%89——%20Dubbo的SPI机制%EF%BC%88中%EF%BC%89.md
122-
)
123-
- [Dubbo底层源码学习(二)—— Dubbo的SPI机制(下)](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Dubbo/Dubbo%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%EF%BC%88%E4%BA%8C%EF%BC%89%E2%80%94%E2%80%94%20Dubbo%E7%9A%84SPI%E6%9C%BA%E5%88%B6%EF%BC%88%E4%B8%8B%EF%BC%89.md)
124-
- Dubbo底层源码学习(三)—— Dubbo的注册中心
125-
- Dubbo底层源码学习(四)—— Dubbo的注册中心缓存机制
126-
- Dubbo底层源码学习(五)—— Dubbo的注册中心重试机制
127-
- [Dubbo底层源码学习(六)—— Dubbo的服务暴露](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Dubbo/Dubbo%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%E2%80%94%E2%80%94%E6%9C%8D%E5%8A%A1%E6%9A%B4%E9%9C%B2.md)
128-
- Dubbo底层源码学习(七)—— Dubbo的服务消费
129-
130116
- Netty底层源码解析
131117
- Netty版本:4.1.43.Final
118+
- [Netty概念扫盲]()
132119
- [二进制运算以及源码、反码以及补码学习](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/Netty/%E4%BA%8C%E8%BF%9B%E5%88%B6.md)
133120
- [Netty源码包结构](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/Netty/Netty%E6%BA%90%E7%A0%81%E5%8C%85%E7%BB%93%E6%9E%84.md)
134121
- [Netty底层源码解析-EventLoopGroup](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/Netty/Netty%E4%B8%AD%E7%9A%84EventLoopGroup%E6%98%AF%E4%BB%80%E4%B9%88.md)

note/Netty/Netty概念扫盲.md

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
# EventLoop
2+
EventLoop 是Netty的事件循环线程模型,它负责:
3+
4+
- 监听 I/O 事件,比如连接、读、写
5+
- 执行Channel相关任务
6+
- 驱动 pipeline 里的 handler 回调
7+
- 保证同一个Channel的事件通常在同一个线程串行执行
8+
9+
10+
11+
EventLoop解决以下问题:
12+
13+
- 高效管理大量连接
14+
- 避免一个连接一个线程的高成本
15+
- 减少并发锁竞争
16+
- 让网络事件处理更有序、可控
17+
18+
19+
20+
一句话:EventLoop 就是 Netty 用来轮询 I/O 事件并执行 Channel 任务的核心线程。
21+
22+
## EventLoop实现
23+
EventLoop是通用抽象,负责:
24+
25+
- 执行任务
26+
- 注册/管理Channel
27+
- 处理 I/O 事件
28+
29+
30+
31+
具体底层怎么等 I/O 事件,由传输实现决定:
32+
33+
- NIO:基于 Java Selector
34+
- epoll:基于 Linux epoll
35+
- kqueue:基于 BSD/macOS kqueue
36+
- io_uring:基于 Linux io_uring
37+
38+
39+
40+
EventLoop 通用调度模型,Transport / IoHandler 决定底层 I/O 轮询实现。
41+
42+
43+
44+
## 网络事件\socket是什么
45+
网络事件就是 socket 状态变化或 I/O 就绪通知。常见包括:
46+
47+
- 有新连接进来:accept
48+
- 连接建立完成:connect
49+
- 有数据可读:read
50+
- 可以继续写数据:write
51+
- 连接关闭或异常:close / error
52+
53+
操作系统告诉 Netty“这个连接现在可以做某件事了”,这就是网络事件。
54+
55+
56+
57+
那socket是什么呢?
58+
59+
Socket是应用程序和网络之间的通信端点。可以理解成:程序用来收发网络数据的“连接句柄”。比如 TCP 通信里:
60+
61+
```plain
62+
客户端 Socket <====网络====> 服务端 Socket
63+
```
64+
65+
程序通过 socket:
66+
67+
- 连接远程服务
68+
- 发送数据
69+
- 接收数据
70+
- 关闭连接
71+
72+
73+
74+
socket 是程序进行网络通信的入口。
75+
76+
# Pipeline
77+
Pipeline 是 Netty 里处理请求/响应的流水线,里面的 Handler 分步骤完成拆包、编解码、序列化、业务分发。Netty Client和Netty Server的pipeline都是用的DefaultChannelPipeline,但是怎么区分是Netty Client的pipeline还是Netty Server的pipeline呢?
78+
79+
靠 Channel 类型和创建来源 区分,不靠 DefaultChannelPipeline 类型区分。DefaultChannelPipeline 本身是通用实现,Client 和 Server 都用它。但它绑定的 Channel 不同:
80+
81+
```plain
82+
客户端连接:
83+
NioSocketChannel
84+
-> new DefaultChannelPipeline(this)
85+
-> Client Pipeline
86+
87+
服务端监听端口:
88+
NioServerSocketChannel
89+
-> new DefaultChannelPipeline(this)
90+
-> Server Boss Pipeline
91+
92+
服务端接收到的客户端连接:
93+
NioSocketChannel
94+
-> new DefaultChannelPipeline(this)
95+
-> Server Child Pipeline
96+
```
97+
98+
也就是说:
99+
100+
```plain
101+
pipeline.channel()
102+
```
103+
104+
Pipeline 类型都一样,属于谁取决于它绑定的是哪个 Channel,以及这个 Channel 是由客户端 Bootstrap创建,还是服务端ServerBootstrap创建。
105+
106+
107+
108+
一个pipeline既可以处理入站请求,也可以处理出站请求,只不过方向不一样。
109+
110+
```plain
111+
入站 Inbound:读数据、解码、业务处理
112+
出站 Outbound:写数据、编码、发送
113+
114+
入站:Head -> Tail
115+
出站:Tail -> Head
116+
```
117+
118+
119+
120+
```plain
121+
一个 ChannelPipeline
122+
┌──────────────────────────────────────────────┐
123+
│ │
124+
│ Head ── H1 ── H2 ── H3 ── H4 ── Tail │
125+
│ │
126+
└──────────────────────────────────────────────┘
127+
128+
129+
入站 Inbound:从 Head 到 Tail
130+
Socket 收到数据
131+
132+
Head ──> H1 ──> H2 ──> H3 ──> H4 ──> Tail
133+
解码 反序列化 业务处理
134+
135+
136+
出站 Outbound:从 Tail 到 Head
137+
业务调用 writeAndFlush()
138+
139+
Tail ──> H4 ──> H3 ──> H2 ──> H1 ──> Head
140+
序列化 编码 写 Socket
141+
```
142+
143+
## 出站和入站
144+
Netty出站——ChannelInboundHandler,是指当前 Channel 将数据写出到网络。比如 RPC Client 发送请求,或 RPC Server 返回响应,都会经过出站流程:Java 对象序列化、协议编码/封装,转成 ByteBuf,再通过 Socket 发送出去。
145+
146+
Netty入站——ChannelOutboundHandler,是指当前 Channel 从网络读取数据。比如 RPC Server 接收请求,或 RPC Client 接收响应,都会经过入站流程:读取 ByteBuf,按协议处理粘包半包,解码协议,再反序列化成 Java 对象。
147+
148+
## 为什么入站:HEAD->TAIL,出站:TAIL->HEAD?
149+
核心逻辑就是:**<font style="color:#DF2A3F;">Head 更靠近 Socket / 底层 I/O,Tail 更靠近业务处理端。</font>**
150+
151+
因为 Netty 把 `Head` 设计成**靠近底层 Socket 的入口**`Tail` 设计成**靠近业务处理的末端**
152+
153+
所以网络数据进来时:
154+
155+
```plain
156+
Socket 读到数据
157+
158+
HeadContext
159+
160+
InboundHandler1
161+
162+
InboundHandler2
163+
164+
TailContext
165+
```
166+
167+
也就是:
168+
169+
```plain
170+
Head -> Tail
171+
```
172+
173+
反过来,业务要写数据出去时,是从业务侧往 Socket 走:
174+
175+
```plain
176+
Tail -> Head
177+
```
178+
179+
最后到 `HeadContext`,由它调用底层 unsafe 把数据写到 Socket。
180+
181+
# Channel
182+
Channel是Netty对网络连接/通信端点的抽象,也就是Netty里对 socket 连接或监听端口的封装。它代表一个可进行 I/O 操作的对象,比如:
183+
184+
+ 服务端监听端口:ServerSocketChannel
185+
+ 客户端连接:SocketChannel
186+
+ UDP 通信:DatagramChannel
187+
188+
189+
190+
它主要负责:
191+
192+
+ 读写数据
193+
+ 绑定/连接/关闭
194+
+ 持有关联的EventLoop
195+
+ 持有ChannelPipeline
196+
197+
# EventLoopGroup、EventLoop、Channel关系
198+
在Netty中,一般这样设置:
199+
200+
```plain
201+
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
202+
EventLoopGroup workerGroup = new NioEventLoopGroup();
203+
```
204+
205+
bossGroup:负责监听端口、accept 新连接,通常 1 个线程就够。workerGroup:负责已连接客户端的读写 I/O,默认线程数通常是 CPU核数 * 2。
206+
207+
208+
209+
EventLoop、线程、channel之间的关系:
210+
211+
- 1 个 EventLoop 通常绑定 1 个线程
212+
- 1 个 EventLoopGroup 包含多个 EventLoop
213+
- 1 个 Channel 注册到 1 个 EventLoop
214+
- 1个EventLoop会有多个channel,一个EventLoop会轮训多个channel的事件
215+
216+
217+
218+
在Netty中,一个EventLoop线程不是被某个Channel独占的。它会轮询处理多个Channel的事件:
219+
220+
```plain
221+
EventLoop-1
222+
-> Channel A read
223+
-> Channel B read
224+
-> Channel C write
225+
-> 定时任务
226+
-> 下一轮 select
227+
```
228+
229+
230+
231+
总结:BossGroup 里通常配置一个或少数几个 EventLoop。Netty 服务端调用 bind() 时,会创建并将 XxxServerSocketChannel 注册到 BossGroup 的某个 EventLoop 上。这个 EventLoop 主要负责轮询 XxxServerSocketChannel 上的客户端新连接事件,也就是 TCP accept 事件。当 accept 到新连接后,Netty 会创建对应的 XxxSocketChannel,并将它注册到 WorkerGroup 按策略选出的某个 EventLoop 上。后续该客户端连接的 read/write 事件,都由这个 XxxSocketChannel 绑定的 Worker EventLoop 负责轮询和处理。
232+
233+
不同的EventLoop底层使用的轮询机制不一样,不管底层是用的:Selector / epoll / kqueue / io_uring / 阻塞 I/O,都需要轮询事件。
234+
235+
- NioEventLoop -> Selector
236+
- EpollEventLoop -> Linux epoll
237+
- KQueueEventLoop -> BSD/macOS kqueue
238+
- IoUringEventLoop -> Linux io_uring
239+
- OioEventLoop -> 阻塞 I/O 模型
240+
241+
242+

0 commit comments

Comments
 (0)