Netty 常见组件介绍
Netty 常见组件介绍
上篇文章Netty入门程序echo 基本包含了Netty常见的组件,本文分别介绍各个组件
- Bootstrap or ServerBootstrap
- EventLoop
- EventLoopGroup
- ChannelPipeline
- Channel
- Future or ChannelFuture
- ChannelInitializer
- ChannelHandler
Bootstrap vs ServerBootstrap
Bootstrap
Bootstrap 主要用于客户端的配置,它主要用于创建一个连接到远程服务器的客户端应用程序
它允许你设置以下内容
- EventLoopGroup
- Channel
- ChannelInitializer 用来配置新创建的通道的 ChannelPipeline。
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("decoder", new StringDecoder());
ch.pipeline().addLast("encoder", new StringEncoder());
ch.pipeline().addLast(new ClientHandler());
}
});
// 连接到服务器
ChannelFuture f = b.connect(host, port).sync();
ServerBootstrap
ServerBootstrap 是专门用于配置和启动服务端通道的工具类。
它用于创建一个能够监听指定端口,接收客户端连接并处理客户端请求的服务器应用程序
ServerBootstrap还需要配置
childHandler 服务端会为每个客户端连接创建一个新的通道,childHandler方法用于添加处理这些客户端通道事件的ChannelHandler。
ServerBootstrap 需要两个 EventLoopGroup,一个用于接受连接
另一个用于处理 IO读写操作,而Bootstrap只需要指定一个EventLoopGroup用于处理IO读写操作
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("decoder", new StringDecoder());
ch.pipeline().addLast("encoder", new StringEncoder());
ch.pipeline().addLast(new ServerHandler());
}
});
ChannelFuture f = b.bind(port).sync();
Channel
Channel 在 Netty 中是对网络 I/O 操作的抽象。它代表了一个网络连接或者通信管道,可以是服务器端用于监听客户端连接的通道,也可以是客户端用于连接服务器的通道。
- NioSocketChannel
基于 Java NIO 的 Socket 通道,主要用于客户端建立 TCP 连接。前面Bootstrap客户端代码片段使用的就是NioSocketChannel
- NioServerSocketChannel
同样基于 Java NIO,用于服务器端监听 TCP 端口,等待客户端的连接请求。前面ServerBootstrap代码片段使用的就是NioSocketChannel
- EpollSocketChannel 和 EpollServerSocketChannel(适用于 Linux)
这两种通道是基于 Linux 的 epoll 机制实现的。
EpollSocketChannel用于客户端连接,EpollServerSocketChannel用于服务器监听
- OioSocketChannel 和 OioServerSocketChannel(基于旧的阻塞 I/O)
这是基于传统的阻塞式 I/O 实现的通道,使用的比较少,通常我们使用NIO,都使用Netty,还用阻塞IO,小编实在编不出有什么合适的理由。
ChannelPipeline
ChannelPipeline 是 Netty 中的一个核心组件,它是一个ChannelHandler 的责任链,负责处理所有的入站事件(读)和出站事件(写)。 无论服务端还是客户端都可能要处理读写事件,因为本质建立在TCP链接基础上,是全双工的。
ChannelPipeline无论在服务端还是客户端,它的设置都非常重要。
ChannelPipeline 包含着一系列的 ChannelHandler,服务端/客户端按照ChannelHandler顺序来处理 IO 事件,它的顺序非常重要。
事件处理顺序
责任链中隐含两个Handler,一个Header,一个是Tail,这两个非常重要,本文先点到为止。
加上Header和Tail用户向ChannelPipeline中添加的ChannelHandler1,ChannelHandler2,…,ChannelHandlerN ; 按顺序处理出站/入站事件。
入站事件顺序
入站事件从 ChannelPipeline 的头部开始,向尾部传播
Header -> ChannelHandler1 -> ChannelHandler2 -> … -> ChannelHandlerN -> Tail
出站事件顺序
出站事件则从ChannelPipeline尾部开始,向头部传播。
Tail -> ChannelHandlerN -> … -> ChannelHandler2 -> ChannelHandler1 -> Header
只有理解了ChannelPipeline的处理顺序,我们才能正确的设置责任链上 ChannelHandler,才能正确的处理对应事件。
ChannelInitializer
ChannelInitializer 是 Netty 中一个非常重要的组件, 它就像是一个 ChannelPipeline 的构建器
ChannelFuture
jdk线程池异步执行需要返回结果的 通常返回的是Future,
ChannelFuture 接口继承 Future,
ChannelFuture 是 Netty 中用于表示一个异步操作结果的接口。在 Netty 中,许多 I/O 操作(如连接建立、数据写入、绑定端口等)都是异步执行的。
ChannelHandler
ChannelHandler 是 Netty 中用于处理 Channel 事件的核心组件。它是一个接口,定义了一系列方法来处理各种网络 I/O 事件,
如数据读取、写入、连接建立和关闭等。可以把 ChannelHandler 看作是一个事件处理器
-
ChannelInboundHandler:
处理入站事件,如连接建立、数据接收、异常捕获等。
常见实现:SimpleChannelInboundHandler, ChannelInboundHandlerAdapter -
ChannelOutboundHandler:
处理出站事件,如绑定、连接、写入数据等。
常见实现:ChannelOutboundHandlerAdapter -
ChannelDuplexHandler:
同时处理入站和出站事件。
常见实现:ChannelDuplexHandler
上面列的几个类比较底层,Netty 内置了许多ChannelHandler ,如果使用Netty开发某些功能肯定会打交道,我们只需要专学处理消息的读写即可。以下列举几个典型的后续大部分都会接触。
- ByteToMessageDecoder
它是一个抽象类,它实现了 ChannelInboundHandler 接口,底层将数据读到ByteBuf中,我们需要将ByteBuf 转换为业务对象,再传递给后续处理器处理。
与之相对应的是MessageToByteEncoder ,将业务消息对象编码为"ByteBuf",再发送给客户端(出站事件)
StringDecoder 和 StringEncoder 就是对应的实现类。 我们基于ByteToMessageDecoder也能实现内置的处理器的能力
public class MyByteToMessageDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
msg = doDecode(in);
out.add(msg);
}
}
其他高级的内置处理器,我们在处理半包,粘包问题可以结合使用。
- DelimiterBasedFrameDecoder
基于分隔符的解码器,用于处理以特定字符或字符序列分隔的消息。适用于消息以特定字符分隔的场景。 能够处理TCP 半包问题
- LineBasedFrameDecoder
基于行的解码器,用于处理以换行符分隔的消息。适用于消息以换行符分隔的场景。可以看成是特殊的基于分隔符的解码器
- FixedLengthFrameDecoder
固定长度的帧解码器,用于处理固定长度的消息。适用于消息长度固定的场景。能够处理TCP 半包问题
总结
Netty 组件看上去很多,实际上我们通常只需要关注 消息解码、编码 和读写事件的处理,这也是选择Netty 原因,简单同时兼顾性能。