Netty入门
前言:
传统的 IO 模型中,一个线程对应一个连接,当有一个新连接时会创建一个新的线程,死循环接收数据。大多情况时,只有少量时间是有数据可读的,因此会造成资源浪费。
在 NIO 模型中一个死循环检测多个连接是否有数据可读,这就是 selector,然后通过检查 selector 知道那些线程是有数据可读的,进而读取数据。
Java 中 NIO 使用比较繁琐,因而使用已经被封装的使用起来非常方便的框架—— Netty 。
传统IO 和 NIO 处理多个连接的图示:
【使用阻塞 I/O 处理多个连接】
【使用 Selector 的非阻塞 I/O】
1. Netty 是什么
Netty 是一个 异步事件驱动的网络应用框架,用于快速开发可维护的高性能服务器和客户端。
异步:异步非阻塞 I/O
事件驱动:当有事件触发时,才会调用处理器进行数据处理
网络应用框架:网络编程的支持
2. Netty 中的 Reactor 线程模型
前言中提到了传统阻塞的I/O 线程,Netty 中开多个线程,每个线程管理着一批连接。
Reactor 线程模型可分为单线程版 多线程版和主从模型版
2.1 单线程模型
单线程版: 一个Reactor线程负责连接,负责分发请求,顺序处理每个连接的业务逻辑。虽然一次可以处理多个请求,但实现上还是一个线程完成所有的任务。不适合多核CPU。
代码实现方式:
private EventLoopGroup group = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap()
.group(group)
.childHandler(new HeartbeatInitializer());
2.2 多线程模型
多线程版: 相比上一种模式,该模型在分配处理部分采用了多线程(线程池),可以解决 CPU 密集型的业务逻辑。但是在Acceptor连接线程上还是单个,如果服务是高I/O 的话还是不能满足需求,也没办法利用多核。
代码实现方式:
private EventLoopGroup boss = new NioEventLoopGroup(1);
private EventLoopGroup work = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap()
.group(boss,work)
.childHandler(new HeartbeatInitializer());
2.3 主从模型版
主从模型:该模型相比第二种模型,是将Reactor分成两部分,mainReactor负责监听server socket,accept新连接;并将建立的socket分派给subReactor。subReactor负责多路分离已连接的socket,读写网络数据,对业务处理功能,其扔给worker线程池完成。通常,subReactor个数上可与CPU个数等同或为核数的两倍。
2.4 Netty 主从多线程模型
Netty 监听客户端连接是一个线程组管理的,而监听客户端读写的任务是由另一个线程组来管理的。Netty 的线程模型是 Reactor 模型的变种,那就是去掉线程池的第三种形式的变种,这也是 Netty NIO的默认模式。
代码实现:
private EventLoopGroup boss = new NioEventLoopGroup();
private EventLoopGroup work = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap()
.group(boss,work)
.childHandler(new HeartbeatInitializer());
在处理时也可加线程池进行处理,使其成为第三种模型。
3. Netty 基本概念
客户端和服务端连接图示:
3.1 NioEventLoop
服务端监听的端口,传统的 Server Socket。
开启两种线程,一种监听客户端连接,一种处理客户端读写。
3.2 Channel
客户端和服务端连接的封装,可以理解为Socket
在Channel连接的封装上面可以进行读写。
3.3 ChannelHandler
Pipeline 中的 ChannelHandler
对数据进行业务处理的逻辑。在逻辑处理链Pipeline上的逻辑处理。
3.4 Decoder 和 Encoder
编码和解码,基于TCP 通信的数据包是基于二进制的。客户端需要将一个Java对象按照通信协议编码为一个二进制数据包,服务端接受后需要进行拆包 将二进制数据包解码为Java对象,交给业务处理。处理后按照同样的通信协议进行编码给客户端返回。
3.5 ByteBuf
ByteBuf 是 Netty 对二进制数据抽象的数据结构,可以更方便的操作二进制数据。
3.6 Pipeline
Channel 上面逻辑处理链