线程组初始化
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(1);
流程如下:
Netty启动器初始化
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(new ChannelHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
// Bind and start to accept incoming connections.
ChannelFuture f = b.bind(port).sync();
b.bind(port)为启动命令,前面的都是配置项,流程如下:
流程图中有一个:将channel注册到父线程组中的一个子线程的selector中,其实一开始不是很理解,明明是一个线程池(意味着可以有多个子线程),那为什么,channel只绑定到其中一个子线程的selector上呢,那其他的子线程做什么用呢?还是说我哪里的理解的有问题,后来看到网上有这么一段说法确认了没有看错源码,解释如下:绑定一个端口,就会把链接这个端口的NioServerSocketChannel注册到一个boss线程池中的某一个子线程的selector上,如果同时绑定多个端口,那此时boss线程池中的其他子线程就用处了,然后我尝试了一下:
ChannelFuture f = b.bind(8088).sync(); // (7)
ChannelFuture f2 = b.bind(8089).sync(); // (7)
通过这样让一个服务端同时绑定了8088端口和8089端口,此时通过http访问,两个端口都是通的,也就是一个Netty服务端是可以监听多个端口的。
Netty运行
Netty的运行步骤,其实和多reactor多线程模型大同小异,首先boss线程池中的子线程开启一个死循环,进行监听来自客户端的请求,流程如下:
boss线程池中的线程监听来自客户端的请求,并把具体的处理工作又注册给了worker线程池中的线程,然后自己再次监听客户端的请求。而worker线程池中的线程同样进入一个死循环,不断监听boss线程池注册过来的任务。
其实我们可以看到boss线程池中的线程和work线程池中的线程做的事情其实差不多,除了责任链,boss的责任链是Netty自己加了往worker线程池注册任务的一环,而work的责任链则是涉及到具体的业务逻辑了,所以我们可以通过增加责任链的环节来进行不同的处理。
Netty结构
ServerBootStrap就是Netty的启动器,一个Netty服务所有内容都在里面,EventLoopGroup则是线程组,ServerBootStrap中有两个线程组,一个boss负责监听客户端连接,一个worker负责具体的数据接收和处理。NioEventLoop为线程组中的子线程,一个EventLoopGroup中可以有很多个NioEventLoop,根据最开始线程池初始化里设置决定的。ChannelPipeline为责任链容器,每对一个channel进行数据处理时就会产生一个ChannelPipeline,所以NioEventLoop中也会有多个ChannelPipeline,但是同一时刻,一个NioEventLoop中只会有一个ChannelPipeline。最后的ChannelHandler就是责任链中的每一环被称作事件,一个责任链上可以有很多个事件。
责任链模式
上面多处说到责任链,其实这是一种设计模式,就是将处理流程设计成一个链条,每个处理操作都是这个链条中的一环,还是比较好理解的。这种模式能有效的降低业务中各个操作的耦合性。Netty的一条责任链中有一个分为两类事件,一种为入站事件,一种为出站事件。只有数据从客户端进入服务器的过程才会触发入栈事件,同样只有只有数据从服务端返回客户端的时候才会触发出栈事件。
ch.pipeline().addLast(new ChannelHandler());
我们上面初始化的代码中就这么一行,其实就是在责任链中加入一个我们自定义的事件。
Netty的责任链有头和尾,数据进入服务器时从头部往尾部依次执行,数据从服务端返回客户端时,从尾部往头部依次执行。
另外Netty的责任链通过在每一个事件中调用next()来执行下一个事件,所以如果在某一个实践中没有手动继续往下执行,那么责任链就不会执行剩下的事件了。