Netty服务端创建时序图
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
BossEventLooPGroup和WorkerEventLoopGroup。BossEventLoopGroup通常是一个单线程的EventLoop,EventLoop维护着一个注册了ServerSocketChannel的Selector实例,BoosEventLoop不断轮询Selector将连接事件分离出来,通常是OP_ACCEPT事件,然后将accept得到的SocketChannel交给WorkerEventLoopGroup。WorkerEventLoopGroup会选择其中一个EventLoopGroup来将这个SocketChannel注册到其维护的Selector并对其后续的IO事件进行处理。在Reactor模式中BossEventLoopGroup主要是对多线程的扩展,而每个EventLoop的实现涵盖IO事件的分离和分发。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;
}
public B channel(Class extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
return channelFactory(new BootstrapChannelFactory(channelClass));
}
@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参数
5.链路建立时创建ChannelPipeline
6.设置ServerBootstrap的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
() { @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
, Object> options = options(); //设置相关参数 synchronized (options) { channel.config().setOptions(options); } final Map , Object> attrs = attrs(); synchronized (attrs) { for (Entry , Object> e: attrs.entrySet()) { @SuppressWarnings("unchecked") AttributeKey 可以看到,初始化工作主要包含三点:(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。