Netty的组件和设计

Netty是基于Java NIO的异步事件驱动实现,保证了高负载下应用程序性能的最大化和可伸缩性。其次,Netty也包含了一组设计模式,将应用程序从网络层解耦,简化了开发的过程,提高了代码的重用性。

Netty的核心组件主要包括:Channel、EventLoop、ChannelFuture、ChannelHandler和ChannelPipeline等。下面我们详细的介绍一下各个组件的用途。

Channel接口

Channel接口提供了bind()、connect()、read()、write(),其操作需要依赖底层网络传输所提供的原语。在Java网络编程中,基本构造是Socket。而Netty的Channel接口所提供的API,大大降低了直接使用Socket的复杂性。Channel接口的常用实现包含:NioSocketChannel、NioDatagramChannel、LocalServerChannel等。

EventLoop接口

EventLoop定义了Netty的核心抽象,用于处理连接的生命周期中所发生的事件。下图展示了Channel、EventLoop、Thread以及EventLoopGroup之间的关系。

一个EventLoopGroup包含一个或多个EventLoop

一个EventLoop在它的生命周期内只和一个Thread绑定

所有由EventLoop处理的IO事件都将在它专有的Thread上被处理

一个Channel在它的生命周期内只注册于一个EventLoop

一个EventLoop可能会被分配给一个或多个Channel

在这种设计中,一个给定的Channel的IO操作都是由相同的Thread执行的,消除了对同步的需要。

ChannelFuture接口

Netty中所有的IO操作都是异步的,所以IO操作不会立即返回。为此,Netty提供了ChannelFuture接口,用于完成IO操作后得到操作结果。

ChannelHandler接口

从研发人员的角度看,我们写的大部分逻辑都是实现ChannelHandler接口,它处理了所有入站和出站的业务逻辑。ChannelHandler有2个子接口:ChannelInboundHandler和ChannelOutboundHandler,分别对应入站和出站操作。在ChannelInboundHandler的实现中,通常会向Channel中写入响应的消息。然后消息数据在发从出去之前,会经过ChannelOutboundHandler的处理,然后再发送出去。

​​​​​​​

ChannelPipeline接口

ChannelPipeline提供了ChannelHandler链的容器,并定义了用于在该链上传播入站和出站的事件流API。当Channel被创建时,它会被自动的分配到专属的ChannelPipeline。ChannelHandler安装到ChannelPipeline的过程如下:

一个ChannelInitializer的实现被注册到了ServerBootstrap中

当ChannelInitializer.initChannel()被调用时,ChannelInitializer将在ChannelPipeline中安装一组自定义的ChannelHandler

ChannelInitializer将自己从ChannelPipeline中移除

下图展示了Netty应用程序的入站和出站流程。假设下图为服务端,接收客户端请求为入站(依次经过各个ChannelInboundHandler),将消息写回到客户端为出站(依次经过各个ChannelOutboundHandler)。

上图展示了出站和入站的ChannelHandler可以被安装到同一个ChannelPipeline。如果一个入站事件被读取,它将会从ChannelPipeline的头部开始流动,并被传递给第一个ChannelInboundHandler。然后依次经过各个匹配的ChannelInboundHandler,直到ChannelPipeline的尾部。类似的,出站事件将从尾部开始,依次经过各个匹配的ChannelOutboundHandler直到头部,然后发送给客户端。

ChannelHandler被添加到ChannelPipeline时,将会被分配一个ChannelHandlerContext,代表了ChannelHandler与ChannelPipeline之间的绑定。虽然这个对象可以被用于获取底层的Channel,但是主要还是用于写出站数据。在Netty中,写消息的方式有2种:channel.write()和context.write()。前一种方式会从ChannelPipeline的尾端开始流动,后一种会从当前Handler向下一个Handler流动(向头部流动)。

ChannelHandler的实现

Netty以适配器的形式提供了大量的默认ChannelHandler实现,目的是简化应用程序的开发。Netty已经实现的常用适配器如下:ChannelHandlerAdapter、ChannelInboundHandlerAdapter、ChannelOutboundHandlerAdapter。

编码器和解码器

当使用Netty接收消息的时候,入站消息就会被解码,也就是将字节转换为字符或对象等格式。当消息出站时,程序对象需要转换为字节才可以传播到网络中去。为此Netty分别为编码器和解码器提供了不同的抽象:ByteToMessageDecoder和MessageToByteEncoder。

SimpleChannelInboundHandler

SimpleChannelInboundHandler是一个抽象类,通常用于客户端的实现。改类继承自ChannelInboundHandlerAdapter,并重写了channelRead()方法。在重写的方法中自动释放了所处理的数据,因此在服务端的实现中并不推荐使用,因为自动释放资源后,无法向县一个Handler传递数据。相应的,客户端实现相对简单一些,但是也需要考虑实际的应用场景去取舍。

BootStrap引导

Netty的引导类为网络层配置提供了容器,包括了服务端的将进程绑定到一个指定的端口(服务端),或者将进程连接到一个远程的主机端口(客户端)。客户端(Bootstrap)创建是需要一个EventLoopGroup,服务端(ServerBootstrap)通常需要两个EventLoopGroup。下面我们看下服务端的引导结构。


服务端需要两组不同的Channel,第一组只包含1个ServerChannel,表示服务器自身的已绑定到某个本地端口的监听套接字。第二组包含所有已经创建的用来处理客户端连接的Channel。


参考:《Netty实战》