因为你底层是使用EventLoopGroup创建线程池使用Threads(多线程)处理 I/O 事件,实际上再往底层钻是:EventLoop进行事件处理 ,以事件为中心(其实就是回调监听),并且使用Channel是一个底层为Nio的I/O处理器,Nio是一个同步的非阻塞模型,进行流程操作;

如图,每一channel对应一个eventloop进行事件处理,并且eventloop是存储在event loop Group中的
Bootstrapping 有两种类型,一种是用于客户端的Bootstrap,一种是用于服务端的ServerBootstrap。不管程序使用哪种协议,无论是创建一个客户端还是服务器都需要使用“引导”。
两种 Bootstrapping 之间有一些相似之处,也有一些不同。Bootstrap 和 ServerBootstrap 之间的差异如下:
| 分类 | Bootstrap | ServerBootstrap |
|---|---|---|
| 网络功能 | 连接到远程主机和端口 | 绑定本地端口 |
| EventLoopGroup | 1 | 2 |
| 实现 | “ServerBootstrap”在服务器监听一个端口,轮询客户端的“Bootstrap”或DatagramChannel是否连接服务器。通常需要调用“Bootstrap”类的connect()方法,但是也可以先调用bind()再调用connect()进行连接,之后使用的Channel包含在bind()返回的ChannelFuture中。 | “Bootstrap”去主动链接一个服务端,连接完成后,使用的Channel包含在bind()返回的ChannelFuture中。 |
概括性的部分在第一个问题中就说到了,这里就不在赘述,本问题主要讨论具体使用场景。
其实ChannelPipeline就是一个单向链表,其中的每一个node就是ChannelHandler 。数据从出到入,都是按照你当时添加ChannelHandler 的顺序去处理的。
ChannelHandler分为两种:ChannelOutboundHandler(出站处理器),ChannelInboundHandler(入站处理器)

每一个Handler中都有一个ChannelHandlerContext 代表着每一个处理器的上下文,这样你就可以继承:抽象基类 ChannelInboundHandlerAdapter 和ChannelOutboundHandlerAdapter,中的每一个监听方法(钩子函数)进行数据的处理。
当 ChannelHandler 被添加到的 ChannelPipeline 后,它将会得到一个 ChannelHandlerContext,它代表一个 ChannelHandler 和 ChannelPipeline 之间的“绑定”。它通常是安全保存对此对象的引用,除了当协议中的使用的是不面向连接(例如,UDP)。而该对象可以被用来获得底层 Channel。
还有,实际上,在 Netty 发送消息有两种方式:1.可以直接写消息给 Channel 、2.写入 ChannelHandlerContext 对象。主要的区别是, 前一种方法会导致消息从 ChannelPipeline的尾部开始,而 后者导致消息从 ChannelPipeline 下一个ChannelHandler 开始。因为你在ChannelPipeline 中才可以获取到ChannelHandlerContext 对象。
核心就是编码器、解码器和一些钩子函数(被封装在适配器中,你可以继承ChannelInboundHandlerAdapter或者直接使用, ChannelInboundandlerAdapter的子类SimpleChannelInboundHandler)
当您发送或接收消息时,Netty 数据转换就发生了。入站消息将从字节转为一个Java对象;也就是说,“解码”。如果该消息是出站相反会发生:“编码”,从一个Java对象转为字节。其原因是简单的:网络数据是一系列字节,因此需要从那类型进行转换。
对于入站数据,主要查看channelRead 方法被覆盖。该方法在每个消息从入站 Channel 读入时调用。该方法将调用特定解码器的“解码”方法,并将解码后的消息转发到管道中下个的 ChannelInboundHandler。
出站消息是类似的。编码器将消息转为字节,转发到下个的 ChannelOutboundHandler。
实现零拷贝,是我们编码、解码后的数据留存的地方,底层是一个动态数组,完美解决了半包以及粘包的问题。
也许最常见的处理器是接收到解码后的消息并应用一些业务逻辑到这些数据。要创建这样一个 ChannelHandler,你只需要扩展基类SimpleChannelInboundHandler 其中 T 是想要进行处理的类型。这样的处理器,你将覆盖基类的一个或多个方法,将获得被作为输入参数传递所有方法的 ChannelHandlerContext 的引用。
在这种类型的处理器方法中的最重要是 channelRead(ChannelHandlerContext chx,T t)。在这个调用中,T 是将要处理的消息。 你怎么做,完全取决于你,但无论如何你不能阻塞 I/O线程,因为这可能是不利于高性能。
阻塞操作
I/O 线程一定不能完全阻塞,因此禁止任何直接阻塞操作在你的 ChannelHandler, 有一种方法来实现这一要求。你可以指定一个 EventExecutorGroup 当添加 ChannelHandler 到ChannelPipeline。此 EventExecutorGroup 将用于获得EventExecutor,将执行所有的 ChannelHandler 的方法。这EventExecutor 将从 I/O 线程使用不同的线程,从而释放EventLoop。
看完本文章,相信你对netty的基础组件就有了大概的认识,从Bootstrap封装最基础的网络连接(Socket)到EventLoopGroup(多线程,线程池模型)中的每一个EventLoop去把控全局,创建Channel和对应的处理器,再到ChannelHandle放置在ChannelPipeline形成处理链对出站入站数据进行处理,再到最小单元Channel的出现,封装了NIO让我们的I/O处理变成非阻塞的状态。每一步的环环相扣,形成了我们现在最流行的网络处理框架,目前大量流行的框架都是基于Netty的,比如:Dubbo,Rocketmq,所以学好他是有必要的,希望大家能够有所收获。
