前置概念
同步和异步
同步:多个事物不能同时进行
异步:多个事物可以同时进行
阻塞和非阻塞
阻塞:遇到障碍从而无法继续运行,等待是阻塞的副作用,等待指的是等待某些相关事物,而不是整个线程都在等待。
非阻塞:没有障碍畅行无阻
同步/异步:指的是能不能同时进行
阻塞/非阻塞:指的是能不能进行
同步阻塞:不能同时进行且等待,相当于单线程等待
同步非阻塞:单线程运行
异步阻塞:同时进行且等待,相当于多线程都在等待
异步非阻塞:同时进行且正常运行,相当于多线程运行
IO
IO指的是读/写数据的过程,和等待读入/写出的过程。一旦拿到数据就变成数据操作了,不是IO。
拿网络IO举例子:等待的过程就是数据从网络到网卡再到内核空间,读写的过程就是内核空间和用户空间相互拷贝。

阻塞IO和非阻塞IO
应用程序都是运行在用户空间的,数据没有到用户空间时应用程序是操作不了的。
阻塞IO:数据没有到达用户空间时,用户线程会阻塞并等待
非阻塞IO:不阻塞用户线程,而是数据到达后才通知用户线程
同步IO和同步阻塞IO
同步IO:发起请求的IO线程,必须等待IO操作完成后才能继续执行该线程的后续代码
有一点需要注意:在等待数据的过程中,线程采用死循环的方式轮询,在读写数据的过程中,线程在堵塞,这依然是同步阻塞IO。
所以,同步IO就是同步阻塞IO
异步IO和异步非阻塞IO
异步IO:发起IO请求的线程无需等待IO完成仍可继续运行
只有异步非阻塞IO
BIO
阻塞IO(Blocking IO)
一个线程管理一个连接
NIO
非阻塞IO(New IO)
单线程可以管理大量连接
一段简单的类比
想象一个很大的快递中心:
1 | Socket/连接 = 每个包裹的收发窗口(channel) |
Netty 将网络编程的复杂性,全都封装到这些角色里面,开发者只需要专注写“处理逻辑”,即收到包裹后需要做什么。
最重要的一点就是:一个线程监听多个channel,一个channel 只属于一个线程
核心组件
channel:
- 表示一个IO对象,比如一条TCP连接
Eventloop & Eventloop Group
- Eventloop : 单线程+selector 循环。一个Eventloop(线程)管理一组channel的IO事件并执行对应的handler
- Eventloop Group:一组Eventloop。常见 bossGroup (accept,即接收连接) 和 workerGroup(读写)
什么是selector?
其实就相当于一个叫号系统,哪个桌子有事件,屏幕就响起对应号码。
- 调用select阻塞等待事件
- 有事件后遍历处理
- 执行普通任务
- 再次select
selector带来了多路复用,一个线程处理大量连接,避免线程上下文切换,事件驱动,无需轮询。
总结
selector是NIO中的多路复用器,使得一个线程同时监听多个Channel的就绪状态。允许一个线程管理大量连接,通过select返回所有就绪事件,从而实现多路复用。
为什么要分boss 和 worker?
首先类比一下:
想象你开了一家餐厅
进门接待员 = boss,负责安排客人进门、座位
服务员 = worker, 负责点单、上菜
如果你让一个人同时:接待、点菜、送菜
一旦桌子很多,新的连接无法建立,系统会卡死
网络事件分两类:
一是建立连接,属于轻量操作;二是处理业务IO(read/write),属于重量操作;如果让一个线程组去处理者两个业务,会导致accept被阻塞,无法接受新的连接,导致服务器吞吐量急剧下降。
ChannelPipeline & ChannelHandler
- Pipeline:每个 Channel 有一个 pipeline(责任链),把入站/出站事件串联处理。
- Handler : 实现具体逻辑(编码/解码/业务)。分入站(Inbound)和出站(Outbound)。
ByteBuf
- Netty自己的缓冲区
Channel Future
- 承载绑定端口的的异步执行状态:成功、失败等,阻塞当前线程直到绑定成功
总结
Netty就是用事件驱动 + 单线程监听循环 + 高效的内存管理 + 可组合的Pipeline, 把高并发网络编的复杂性封装起来,用极少的线程处理海量的连接。