首先,我们在Java基础中使用的IO流,比如文件读写、网络通信,都属于IO操作。而在学习Netty之前,其实我们应该去学习几种IO概念。比如BIO、NIO等,掌握阻塞、非阻塞、同步、异步的概念。本篇中先介绍Netty的组件,以上的内容我们在后面详细介绍。
(1)EventLoopGroup
前面介绍了几种Netty的线程模型,我们最常用的第三种主从Reactor多线程模型。如下图所示,EventLoopGroup是Netty中的一个核心。这张简易的线程流程图就对应了上一篇文章中所说的主从多线程Reactor模型,分别有两个线程池:EventLoopGroupA和EventLoopGroupB。一个用于接收请求,另一个用于处理IO操作。

代码如下:
|
|
如果只使用一个线程池的话,可以这样写:

其实这个方法最终还是调用了sb.group(bossGroup, workerGroup),只不过bossGroup和workerGroup是同一个对象。那如果使用单线程模型呢?很简单,通过配置参数:io.netty.eventLoopThreads来指定线程的数量,如果不配置则默认是cpu数量的两倍。配置为0或者1则只使用一个线程。
我们参考下面一张图,可以帮我们更好的理解Netty原理。

从上图中可以看出,一个EventLoopGroup就相当于一个线程池,而每一个EventLoop就是一个线程,当新的Channel被创建时(有新的请求进来),就会在EventLoopGroup里注册一下,同时会分配一个EventLoop给这个Channel,从此开始直到这个Channel被销毁,这个Channel只能被它绑定的这个EventLoop执行,这也就是为什么Netty可以不用考虑并发的原因。由此可见,会存在多个Channel去竞争同一个EventLoop的情况,那此时执行线程的处理逻辑是怎样的呢?也很简单:如果Channel所绑定的那个线程是空闲的,则执行任务,否则去排队。一个EventLoopGroup里面可以包含多个EventLoop,顶层都是都基于Java的执行器Executor的,他们的继承关系如下:

(2)ChannelPipeline&ChannelHander
关于接收、分配请求的流程已经说了,接下来就是数据处理了,这是Netty最关键的一部分!Netty采用了一种叫做数据流(data flow)的处理机制,类似于Unix中的管道。即每一个Channel都有一个自己的ChannelPipeline,每一个pipeline里会有多个ChannelHandler。数据会像击鼓传花一样依次通过每一个handler被逐一处理。流程如下图所示:

上图这个流处理是双向混合的,分为Inbound和Outbound(他们在Netty3中被称为upstream和downstream),分别对应request和response。这个handler被分成两类:ChannelOutboundHandler和ChannelInboundHandler。当服务器处理进来的请求时,则只会调用实现了ChannelInboundHandler的handler;当服务器返回信息给客户端时,则只会调用实现了ChannelOutboundHandler的handler。

Handler的使用非常方便,你只需要继承一个ChannelInboundHandlerAdapter,重写一些方法,比如channelRead(),channelReadComplete()和exceptionCaught()等,在channelRead方法里面对进来的数据进行处理,也就是我们处理业务逻辑的地方。或者更简单的继承SimpleChannelInboundHandler,只要重写channelRead0()就可以了。
(3)Encoders&Decoders
我们在解析处理请求时通常需要对数据格式进行转换,比如把字节变成对象,或者把对象转换为字节。针对这种常见的场景,Netty提供了编码和解码的接口:MessageToByteEncoder和ByteToMessageEncoder。其实两个抽象类分别继承了ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter,说白了,使用起来和普通的handler没什么区别。自己写的类只要重写decode()或者encode()方法对数据进行处理即可。
(4)Bootstrap/ServerBootstrap
顾名思义就是启动类,分别负责启动客户端和服务器端。这个类用来配置相关参数,比如设置EventLoopGroup,IO类型,handler等等,Netty的一切都从这里开始。
总结:如何写一个服务器端。
(1)设置主从reactor模式
(2)指定IO类型。
(3)指定handler。这里的EchoServerChannelPipeline类继承了抽象类ChannelInboundHandlerAdapter,并实现其中的方法。

(4)绑定端口,然后添加监听器并实现operationComplete方法。
参考代码如下:

学到这里,大概对Netty的组件以及原理有了了解。后续可以深入学习Netty了。





