Netty服务端创建时序图
- 创建ServerBootstrap实例
ServerBootstrap是一个用于启动Netty服务端的辅助类,提供一系列方法设置启动参数。因为ServerBootstrap需要设置的各项信息很多,所以这里采用builder模式实现。
- 设置并绑定Reactor线程池
通过构造函数创建ServerBootstrap实例之后,通常会创建两个EventLoopGroup(并不是必须要创建两个不同的EventLoopGroup,也可以只创建一个并共享),代码如下所示:
BossEventLooPGroup和WorkerEventLoopGroup。BossEventLoopGroup通常是一个单线程的EventLoop,EventLoop维护着一个注册了ServerSocketChannel的Selector实例,BoosEventLoop不断轮询Selector将连接事件分离出来,通常是OP_ACCEPT事件,然后将accept得到的SocketChannel交给WorkerEventLoopGroup。WorkerEventLoopGroup会选择其中一个EventLoopGroup来将这个SocketChannel注册到其维护的Selector并对其后续的IO事件进行处理。在Reactor模式中BossEventLoopGroup主要是对多线程的扩展,而每个EventLoop的实现涵盖IO事件的分离和分发。EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup();
NioEventLoopGroup实际就是Reactor线程池,负责调度和执行客户端的接入、网络读写事件的处理、用户自定义任务和定时任务的执行。通过ServerBootstrap的group方法将两个EventLoopGroup实例传入,代码如下:public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) { super.group(parentGroup); if (childGroup == null) { throw new NullPointerException("childGroup"); } if (this.childGroup != null) { throw new IllegalStateException("childGroup set already"); } this.childGroup = childGroup; return this; }
Netty为了更好的利用多核CPU的资源对Reactor模式进行了改进,每个Netty服务端程序中有多个EventLoop同时运行,每一个EventLoop维护一个Selector实例,进行Reactor模式的工作。此外,EventLoop的职责除了处理IO事件,还包括处理定时任务和用户自定义任务。Netty还提供了一个ioRatio参数,供使用者调整EventLoop运行不同任务时间的比例。这样的设计避免了多线程并发操作和锁竞争,提升了I/O线程的处理和调度性能。
Netty基于单线程设计的EventLoop能够同时处理成千上万的客户端连接的IO事件,缺点是单线程不能够处理时间过长的任务,这样会阻塞使得IO事件的处理被阻塞,严重的时候回造成IO事件堆积,服务不能够高效响应客户端请求。,当我们遇到需要处理时间很长的任务的时候,我们可以将它交给子线程来处理,主线程继续去EventLoop,当子线程计算完毕再讲结果交给主线程。 - 设置并绑定服务端Channel
线程组和线程类型设置完成后,需要设置服务端Channel,Netty通过Channel工厂类来创建不同类型的Channel,对于服务端,需要创建NioServerSocketChannel,所以,通过指定Channel类型的方式创建Channel工厂。ServerBootstrapChannelFactory是ServerBootstrap的内部静态类,职责是根据Channel的类型通过反射创建Channel的实例,服务端需要创建的是NioServerSocketChannel实例,channel方法代码如下:public B channel(Class<? extends C> channelClass) { if (channelClass == null) { throw new NullPointerException("channelClass"); } return channelFactory(new BootstrapChannelFactory<C>(channelClass)); }
Channel工厂类:
@SuppressWarnings("unchecked") public B channelFactory(ChannelFactory<? extends C> channelFactory) { if (channelFactory == null) { throw new NullPointerException("channelFactory"); } if (this.channelFactory != null) { throw new IllegalStateException("channelFactory set already"); } this.channelFactory = channelFactory; return (B) this; }
4.设置TCP参数
- 指定NioServerSocketChannel后,需要设置TCP的一些参数,作为服务端,主要是要设置TCP的 backlog参数。backlog指定了内核为此套接口排队的最大连接个数,对于给定的监听套接口,内核要维护两个队列,未链接队列和已连接队列,根据TCP三路握手过程中三个分节来分隔这两个队列。
5.链路建立时创建ChannelPipeline
- ChannelPipeline的本质就是一个负责处理网络事件的职责链,负责管理和执行ChannelHandler。每个Channel都持有一个ChannelPipeline,网络事件以事件流的形式在ChannelPipeline中流转,由ChannelPipeline根据ChannelHandler的执行策略调度ChannelHandler的执行。值得注意的是ChannelPipeline并不直接持有ChannelHandler,而是用一个ChannelHandlerContext类包装ChannelHandler。
6.设置ServerBootstrap的handler
- TCP参数设置完成后,用户可以为启动辅助类和其父类分别指定Handler,两类Handler的用途不同,子类中的Hanlder是NioServerSocketChannel对应的ChannelPipeline的Handler,父类中的Hanlder是客户端新接入的连接SocketChannel对应的ChannelPipeline的Handler。两者的区别可以通过下图来展示:
本质区别就是:ServerBootstrap中的Handler是NioServerSocketChannel使用的,所有连接该监听端口的客户端都会执行它,父类AbstractBootstrap中的Handler是个工厂类,它为每个新接入的客户端都创建一个新的Handler。
ChannelHandler是Netty提供给用户定制和扩展的关键接口。利用ChannelHandler,用户可以完成大多数的功能定制,例如消息编解码、心跳、安全认证、TSL/SSL认证、流量控制和流量整形等。Netty同事也提供了一些现成的Channel供使用,如:编解码框架ByteToMessageCodec、基于长度的半包解码器LengthFieldBasedFrameDecoder、日志打印LoggerHandler、SSL安全认证SslHandler、链路空闲检测IdleStateHandler、流量整形ChannelTrafficShapingHandler、Base64编解码Base64Encoder和Base64Decoder等。
添加ChannelHandler的具体方式如下:.childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new HttpRequestDecoder()); ch.pipeline().addLast(new HttpObjectAggregator(Integer.MAX_VALUE)); ch.pipeline().addLast(new HttpResponseEncoder()); ch.pipeline().addLast(new HttpServerInboundHandler()); } });
7.绑定并启动监听端口在绑定监听端口之前系统会做一系列的初始化和检测工作,完成之后,会启动监听端口,并将ServerSocketChannel注册到Selector上监听客户端连接。具体实现如下:initprivate ChannelFuture doBind(final SocketAddress localAddress) { final ChannelFuture regFuture = initAndRegister(); final Channel channel = regFuture.channel(); if (regFuture.cause() != null) { return regFuture; } if (regFuture.isDone()) { ChannelPromise promise = channel.newPromise(); doBind0(regFuture, channel, localAddress, promise); return promise; } else { final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel); regFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { Throwable cause = future.cause(); if (cause != null) { promise.setFailure(cause); } else { promise.executor = channel.eventLoop(); } doBind0(regFuture, channel, localAddress, promise); } }); return promise; } }
void init(Channel channel) throws Exception { final Map<ChannelOption<?>, Object> options = options(); //设置相关参数 synchronized (options) { channel.config().setOptions(options); } final Map<AttributeKey<?>, Object> attrs = attrs(); synchronized (attrs) { for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) { @SuppressWarnings("unchecked") AttributeKey<Object> key = (AttributeKey<Object>) e.getKey(); channel.attr(key).set(e.getValue()); } } //将ServerBootstrap的handler添加到channel的pipeline中 ChannelPipeline p = channel.pipeline(); if (handler() != null) { p.addLast(handler()); } //将用于服务端注册的handler:ServerBootstrapAcceptor注册到channel的pipeline中 final EventLoopGroup currentChildGroup = childGroup; final ChannelHandler currentChildHandler = childHandler; final Entry<ChannelOption<?>, Object>[] currentChildOptions; final Entry<AttributeKey<?>, Object>[] currentChildAttrs; synchronized (childOptions) { currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size())); } synchronized (childAttrs) { currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size())); } p.addLast(new ChannelInitializer<Channel>() { @Override public void initChannel(Channel ch) throws Exception { ch.pipeline().addLast(new ServerBootstrapAcceptor( currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs)); } }); }
可以看到,初始化工作主要包含三点:(1)设置Socket参数;(2)将ServerBootstrap的handler添加到channel的pipeline中;(3)将ServerBootstrapAcceptor注册到channel的pipeline中。
Channel初始化完成之后,在doBind0方法中将Channel绑定到指定端口上:
private static void doBind0( final ChannelFuture regFuture, final Channel channel, final SocketAddress localAddress, final ChannelPromise promise) { //此方法在channelRegistered()被触发前执行,使用者可以覆盖channelRegistered方法来定制pipeline channel.eventLoop().execute(new Runnable() { @Override public void run() { if (regFuture.isSuccess()) { channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); } else { promise.setFailure(regFuture.cause()); } } }); }
8.Selector轮询
到此,Netty服务端监听的相关资源已经初始化完毕,接下来就是讲NioServerSocketChannel注册到Reactor线程的Selector上,然后轮询客户端连接事件。NioServerSocketChannel注册的代码如下:
public final void register(EventLoop eventLoop, final ChannelPromise promise) { //此处代码省略 if (eventLoop.inEventLoop()) { register0(promise); } else { try { eventLoop.execute(new OneTimeTask() { @Override public void run() { register0(promise); } }); } catch (Throwable t) { //此处代码省略 } } }
首先判断该操作是否是由eventLoop自身发起的,如果是就直接注册,否则就将注册操作封装成一个Task放异步队列中执行。此处由于是ServerBootstrap所在线程执行的操作,所以会将Task放到NioEventLoop中执行。具体注册逻辑的代码如下:
真正实现注册到逻辑:private void register0(ChannelPromise promise) { try { if (!promise.setUncancellable() || !ensureOpen(promise)) { return; } doRegister(); registered = true; safeSetSuccess(promise); pipeline.fireChannelRegistered(); if (isActive()) {//判断ServerSocketChannel监听是否成功,如果成功则触发ChannelActive事件 pipeline.fireChannelActive(); } } catch (Throwable t) { //此处代码省略 } }
protected void doRegister() throws Exception { boolean selected = false; for (;;) { try { selectionKey = javaChannel().register(eventLoop().selector, 0, this); return; } catch (CancelledKeyException e) { //此处代码省略 } } }
此处注册时没有注册OP_ACCEPT(16),而是注册0,表示只注册,不监听任何网络事件。这样实现的原因有两点:(1)注册方法是多态的,它可以被NioServerSocketChannel用来监听客户端连接事件,也可以被SocketChannel用来监听网络读或者写操作;(2)监听位可以通过SelectionKey的interestOpos方法方便的修改,所以此处通过register操作获取SelectionKey并给AbstractNioChannel的成员变量selectionKey赋值。
注册成功之后,触发pipelineChannelRegistered事件,ChannelRegistered事件在pipeline中处理后,判断ServerSocketChannel监听是否成功,如果成功则触发ChannelActive事件。isActive方法也是个多态方法,在服务端就是判断监听是否成功,在客户端就是判断TCP连接是否完成。pipeline处理ChannelActive事件,完成之后根据配置决定是否出发Channel的读操作:
public ChannelPipeline fireChannelActive() { head.fireChannelActive(); if (channel.config().isAutoRead()) { channel.read(); } return this; }
AbstractChannel的读操作触发pipeline的读操作,最终调用HeadHandler的读操作。HeadHandler的读操作则是调用channel自身的成员变量unsafe的beginRead方法,unsafe是channel类的一个内部接口,基本上所有的网络I/O操作都是由Unsafe类负责实现的。
protected void doBeginRead() throws Exception { // Channel.read() or ChannelHandlerContext.read() was called if (inputShutdown) { return; } final SelectionKey selectionKey = this.selectionKey; if (!selectionKey.isValid()) { return; } readPending = true; final int interestOps = selectionKey.interestOps(); if ((interestOps & readInterestOp) == 0) { selectionKey.interestOps(interestOps | readInterestOp);//修改注册的操作位 } }
由于不同类型的channel对读操作的准备工作不同,所以doBeginRead也是个多态方法,对于NIO通信,客户端和服务端都需要修改网络监听操作位,对于NioServerSocketChannel来说就是OP_ACCEPT,于是修改注册的操作位为OP_ACCEPT。在doBeginRead方法中是将其修改为readInterestOp,而创建NioServerSocketChannel时会将readInterestOp设置成OP_ACCEPT:
public NioServerSocketChannel(ServerSocketChannel channel) { super(null, channel, SelectionKey.OP_ACCEPT); config = new NioServerSocketChannelConfig(this, javaChannel().socket()); }
9.当轮询到准备就绪的Channel之后,就有Reactor线程执行ChannelPipeline的相应方法,最终调度并执行相应的ChannelHandler,包括Netty系统ChannelHandler和用户自定义的ChannelHandler。