Netty进阶:ServerBootStrap源码分析之服务端启动过程

服务端流程概述

Netty 服务端创建的时序图:

netty

服务器端的启动代码:

public class NettyServer {

    public void bind(int port){
        // 创建EventLoopGroup
        EventLoopGroup bossGroup = new NioEventLoopGroup();        //创建BOSS线程组 用于服务端接受客户端的连接
        EventLoopGroup workerGroup = new NioEventLoopGroup();      //创建WORK线程组 用于进行SocketChannel的网络读写

        try {
            // 创建ServerBootStrap实例
            // ServerBootstrap 用于启动NIO服务端的辅助启动类,目的是降低服务端的开发复杂度
            ServerBootstrap b = new ServerBootstrap();
            // 绑定Reactor线程池
            b.group(bossGroup, workerGroup)
                    // 设置并绑定服务端Channel
                    // 指定所使用的NIO传输的Channel
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .handler(new LoggingServerHandler())
                    .childHandler(new ChannelInitializer(){

                        @Override
                        protected void initChannel(Channel ch) throws Exception {
                            //do something
                        }
                    });

            // 绑定端口,同步等待成功
            ChannelFuture future = b.bind(port).sync();
            // 等待服务端监听端口关闭
            future.channel().closeFuture().sync();

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 优雅地关闭
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    private class LoggingServerHandler extends ChannelInboundHandlerAdapter{
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            System.out.println("loggin-channelActive");
        }

        @Override
        public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
            System.out.println("loggin-channelRegistered");
        }

        @Override
        public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
            System.out.println("loggin-handlerAdded");
        }
    }

    public static void main(String[] args){
        new NettyServer().bind(8899);
    }
}

主要步骤为:

  1. 创建 ServerBootstrap 实例
  2. 设置并绑定 Reactor 线程池
  3. 设置并绑定服务端 Channel
  4. 创建并初始化 ChannelPipeline
  5. 添加并设置 ChannelHandler
  6. 绑定并启动监听端口
1. 创建EventLoopGroup
	EventLoopGroup bossGroup = new NioEventLoopGroup();       
    EventLoopGroup workerGroup = new NioEventLoopGroup();    

bossGroup 为 BOSS 线程组,用于服务端接受客户端的连接, workerGroup 为 worker 线程组,用于进行 SocketChannel 的网络读写。当然也可以创建一个并共享。

创建ServerBootstrap实例

ServerBootstrap b = new ServerBootstrap();

ServerBootStrap为Netty服务端的启动引导类,用于帮助用户快速配置、启动服务端服务。

2. 设置并绑定Reactor线程池

调用 group() 方法,为 ServerBootstrap 实例设置并绑定 Reactor 线程池。

b.group(bossGroup, workerGroup)

当然也可以使用一个b.group(group),因为此方法在ServerBootstrap内部调用了group(bossGroup, workerGroup)方法。

    @Override
    public ServerBootstrap group(EventLoopGroup group) {
        return group(group, group);
    }

EventLoopGroup 为 Netty 线程池,它实际上就是 EventLoop 的数组容器。EventLoop 的职责是处理所有注册到本线程多路复用器 Selector 上的 Channel,Selector 的轮询操作由绑定的 EventLoop 线程 run 方法驱动,在一个循环体内循环执行。通俗点讲就是一个死循环,不断的检测 I/O 事件、处理 I/O 事件。bossGroup 的作用就是不断地接收新的连接,接收之后就丢给 workerGroup 来处理,workerGroup 负责干活就行(负责客户端连接的 IO 操作)。

group方法的源码如下:

  public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
      super.group(parentGroup);
      this.childGroup = childGroup;
      return this;
  }

首先调用了父类的构造方法,并且参数是的bossGroup,然后将成员变量childGroup设置为我们传入的workerGroup。
关于EventLoopGroup的内部原理分析请见EventLoopGroup 源码分析文章。

3. 绑定服务端channel

绑定线程池后,则需要设置 channel 类型,服务端用的是 NioServerSocketChannel 。当然也可以使用BioServerSocketChannel。

.channel(NioServerSocketChannel.class)

此方法是设置AbstractBootstrap的channelFactory属性,ChannelFactory是一个工厂类,利用反射创建NioServerSocketChannel 对象,如下:

    public B channelFactory(ChannelFactory<? extends C> channelFactory) {
        this.channelFactory = channelFactory;
        return self();
    }

    public ReflectiveChannelFactory(Class<? extends T> clazz) {
        if (clazz == null) {
            throw new NullPointerException("clazz");
        }
        this.clazz = clazz;
    }

channelFactory设置完毕后,和是利用此工厂类创建NioServerSocketChannel呢?答案是AbstractBootstrap.doBind -> AbstractBootstrap.initAndRegister->init(channel),在init通过工厂类创建NioServerSocketChannel。而在工厂类中是通过无参的构造函数来实例化一个NioServerSocketChannel。

public T newChannel() {
    try {
        return clazz.getConstructor().newInstance();
    } catch (Throwable t) {
        throw new ChannelException("Unable to create Channel from class " + clazz, t);
    }
}
4. 添加并设置ChannelHandler

设置完 Channel 参数后,用户可以为启动辅助类和其父类分别指定 Handler

.handler(new LoggingServerHandler())
.childHandler(new ChannelInitializer(){
   //省略代码
})

这两个 Handler 不一样,前者(handler())设置的 Handler 是服务端 NioServerSocketChannel的,后者(childHandler())设置的 Handler 是属于每一个新建的 NioSocketChannel 的。跟踪源代码会发现两种所处的类不一样,handler 位于 AbstractBootstrap 中,childHandler 位于 ServerBootstrap 中。AbstracServerBootstrap 中的 Handler 是 NioServerSocketChannel 使用的,所有连接该监听端口的客户端都会执行它。

5. 绑定端口,启动服务

通过b.bind(port)来启动服务,下面将来进行分析。调用 ServerBootstrap 的 bind(int port) 方法进行端口绑定,该方法有调用bind(SocketAddress localAddress)方法。

    public ChannelFuture bind(SocketAddress localAddress) {
        validate();
        if (localAddress == null) {
            throw new NullPointerException("localAddress");
        }
        return doBind(localAddress);
    }    

其中调用validate()和doBind(localAddress)两个方法。这两个方法在
AbstractBootstrap中。

    //函数功能:检查相关参数是否设置了
    @SuppressWarnings("unchecked")
    public B validate() {
        if (group == null) {//这里的group指的是:b.group(bossGroup, workerGroup)代码中的bossGroup,
            throw new IllegalStateException("group not set");
        }

        if (channelFactory == null) {
            throw new IllegalStateException("channel or channelFactory not set");
        }
        return (B) this;
    } 

该方法主要检查了两个参数,一个是group,一个是channelFactory,前面已经提到。其中调用用group方法是将bossGroup赋值给了group,调用将channel()方法将创建ChannelFactory赋值给了channelFactory。

接下来看bind方法中的doBind(localAddress)方法

private ChannelFuture doBind(final SocketAddress localAddress) {
    final ChannelFuture regFuture = initAndRegister();//1
    final Channel channel = regFuture.channel();
    if (regFuture.cause() != null) {
        return regFuture;
    }
    if (regFuture.isDone()) {
        ChannelPromise promise = channel.newPromise();
        doBind0(regFuture, channel, localAddress, promise);//2
        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.registered();
                    doBind0(regFuture, channel, localAddress, promise);
                }
            }
        });
        return promise;
    }
}

doBind这个函数是我们要分析的重点,这个函数的主要工作有如下几点:

1、通过initAndRegister()方法得到一个ChannelFuture的实例regFuture。

2、通过regFuture.cause()方法判断是否在执行initAndRegister方法时产生来异常。如果产生来异常,则直接返回,如果没有产生异常则进行第3步。

3、通过regFuture.isDone()来判断initAndRegister方法是否执行完毕,如果执行完毕来返回true,然后调用doBind0进行socket绑定。如果没有执行完毕则返回false进行第4步。

4、regFuture会添加一个ChannelFutureListener监听,当initAndRegister执行完成时,调用operationComplete方法并执行doBind0进行socket绑定。

第3、4点想干的事就是一个:调用doBind0方法进行socket绑定。

5.1 AbstractBootstrap.doBind -> AbstractBootstrap.initAndRegister
    final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            channel = channelFactory.newChannel();//A
            init(channel);//B
        } catch (Throwable t) {
            if (channel != null) {
                channel.unsafe().closeForcibly();
                return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
            }
            return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
        }

        ChannelFuture regFuture = config().group().register(channel);//C
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }
        return regFuture;
    }
5.1.1 channel = new NioServerSocketChannel()

这里的channel为一个NioServerSocketChannel对象,通过反射使用channelFactory的newChannel()方法,该方法在2节提到过,channelFactory对象是通过channel()方法创建的。

5.1.2 init(channel)
    @Override
    void init(Channel channel) throws Exception {
		//1、设置新接入channel的option
        final Map<ChannelOption<?>, Object> options = options0();
        synchronized (options) {
            setChannelOptions(channel, options, logger);
        }
		//2、设置新接入channel的attr
        final Map<AttributeKey<?>, Object> attrs = attrs0();
        synchronized (attrs) {
            for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
                @SuppressWarnings("unchecked")
                AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
                channel.attr(key).set(e.getValue());
            }
        }
		// 获取绑定的pipeline
        ChannelPipeline p = 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()));
        }

		// 为NioServerSocketChannel的pipeline添加一个初始化Handler,
		// 当NioServerSocketChannel在EventLoop注册成功时,该handler的init方法将被调用
        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(final Channel ch) throws Exception {
                final ChannelPipeline pipeline = ch.pipeline();
                ChannelHandler handler = config.handler();
                if (handler != null) {
                    pipeline.addLast(handler);
                }

                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
 						// 为NioServerSocketChannel的pipeline添加ServerBootstrapAcceptor处理器
                        // 该Handler主要用来将新创建的NioSocketChannel注册到EventLoopGroup中
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });
    }

init()整个过程可以分为三个步骤:

  • 设置 Channel 的 option 和 attr;
  • 获取绑定的 pipeline,然后为 NioServerSocketChannel绑定的 pipeline 添加 Handler;
  • 将用于服务端注册的 Handler ServerBootstrapAcceptor 添加到 ChannelPipeline 中。ServerBootstrapAcceptor 为一个接入器,专门接受新请求,把新的请求扔给某个事件循环器。ServerBootstrapAcceptor是ServerBootstrap的一个静态内部类,ServerBootstrapAcceptor 中重写了 channelRead 方法, 其主要代码如下:
@Override
@SuppressWarnings("unchecked")
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    final Channel child = (Channel) msg;
    child.pipeline().addLast(childHandler);
    setChannelOptions(child, childOptions, logger);
    for (Entry<AttributeKey<?>, Object> e: childAttrs) {
        child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
    }

   childGroup.register(child).addListener(new ChannelFutureListener() {
           @Override
           public void operationComplete(ChannelFuture future) throws Exception {
               if (!future.isSuccess()) {
                   forceClose(child, future.cause());
               }
           }
       });
    }
}

Channel child 是一个 NioSocketChannel 的实例,childGroup 是构造此对象是传入的 currentChildGroup, 即我们的 workerGroup,因此这里的 childGroup.register 就是将 workerGroup 中的某个 EventLoop 和 NioSocketChannel 关联了。, 那么现在的问题是, ServerBootstrapAcceptor.channelRead 方法是怎么被调用的呢? 其实当一个 client 连接到 server 时, Java 底层的 NIO ServerSocketChannel 会有一个 SelectionKey.OP_ACCEPT 就绪事件, 接着就会调用到 NioServerSocketChannel.doReadMessages:

protected int doReadMessages(List<Object> buf) throws Exception {
    SocketChannel ch = javaChannel().accept();
    ... 省略部分代码
    buf.add(new NioSocketChannel(this, ch));
    return 1;
}

在 doReadMessages 中, 通过 javaChannel().accept() 获取到客户端新连接的 SocketChannel, 接着就实例化一个 NioSocketChannel, 并且传入 NioServerSocketChannel 对象(即 this), 由此可知, 我们创建的这个 NioSocketChannel 的父 Channel 就是 NioServerSocketChannel 实例 .

接下来就经由 Netty 的 ChannelPipeline 机制, 将读取事件逐级发送到各个 handler 中, 于是就会触发前面我们提到的 ServerBootstrapAcceptor.channelRead 方法。

5.1.3 ChannelFuture regFuture = config().group().register(channel);

这段代码的功能及时向EventLoopGroup中注册一个channel,注意这里的 group() 返回的是前面的 boss NioEvenLoopGroup,register方法实现在MultithreadEventLoopGroup,他的接口声明在EventLoopGroup中。下面我们进入register方法内部:

 @Override
public ChannelFuture register(Channel channel) {
    return next().register(channel);
}

调用 next() 方法从 EventLoopGroup 中获取下一个 EventLoop,调用 register() 方法注册:

public ChannelFuture register(Channel channel) {
   return register(new DefaultChannelPromise(channel, this));
}

将Channel和EventLoop封装成一个DefaultChannelPromise对象,然后调用register()方法。DefaultChannelPromis为ChannelPromise的默认实现,而ChannelPromisee继承Future,具备异步执行结构,绑定Channel,所以又具备了监听的能力,故而ChannelPromise是Netty异步执行的核心接口。

public ChannelFuture register(ChannelPromise promise) {
    ObjectUtil.checkNotNull(promise, "promise");
    promise.channel().unsafe().register(this, promise);
    return promise;
}

public final void register(EventLoop eventLoop, final ChannelPromise promise) {
    if (!isCompatible(eventLoop)) {
        promise.setFailure(
                new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
        return;
    }
    AbstractChannel.this.eventLoop = eventLoop;
    // 必须要保证注册是由该EventLoop发起的
    if (eventLoop.inEventLoop()) {
        register0(promise);        // 注册
    } else {
        // 如果不是单独封装成一个task异步执行
        try {
            eventLoop.execute(new Runnable() {
                @Override
                public void run() {
                    register0(promise);
                }
            });
        } catch (Throwable t) {
            logger.warn(
                    "Force-closing a channel whose registration task was not accepted by an event loop: {}",
                    AbstractChannel.this, t);
            closeForcibly();
            closeFuture.setClosed();
            safeSetFailure(promise, t);
        }
    }
}

过程如下:

  1. 首先通过isRegistered() 判断该 Channel 是否已经注册到 EventLoop 中;
  2. 通过 eventLoop.inEventLoop() 来判断当前线程是否为该 EventLoop 自身发起的,如果是,则调用 register0() 直接注册;
  3. 如果不是,说明该 EventLoop 中的线程此时没有执行权,则需要新建一个线程,单独封装一个 Task,而该 Task 的主要任务则是执行 register0()。

无论当前 EventLoop 的线程是否拥有执行权,最终都会要执行 register0(),如下:

private void register0(ChannelPromise promise) {
	boolean firstRegistration = neverRegistered;
	// 真正的注册动作
	doRegister();
	neverRegistered = false;
	registered = true;
	pipeline.invokeHandlerAddedIfNeeded();
	safeSetSuccess(promise);
	pipeline.fireChannelRegistered();
	//如果Channel已经绑定端口、或者Connect/open,在ServerSocketChannel的注册过程不会执行。目前为止只是将ServerSocketChannel注册到Selector,并没有绑定端口。
	if (isActive()) {
		if (firstRegistration) {
		pipeline.fireChannelActive();
		} else if (config().isAutoRead()) {
		beginRead();
		}
	}
}

如果 Channel 处于 open 状态,则调用 doRegister() 方法完成注册,然后将注册结果设置为成功。最后判断如果是首次注册且处于激活状态,则发起 pipeline 的 fireChannelActive()。

   protected void doRegister() throws Exception {
        boolean selected = false;
        for (;;) {
            try {
                // 注册到NIOEventLoop的Selector上
                selectionKey = javaChannel().register(eventLoop().selector, 0, this);
                return;
            } catch (CancelledKeyException e) {
                if (!selected) {
                    eventLoop().selectNow();
                    selected = true;
                } else {
                    throw e;
                }
            }
        }
    }

这里注册时 ops 设置的是 0,也就是说 ServerSocketChannel 仅仅只是表示了注册成功,还不能监听任何网络操作,这样做的目的是(摘自《Netty权威指南(第二版)》):

  1. 注册方式是多态的,它既可以被 NIOServerSocketChannel 用来监听客户端的连接接入,也可以注册 SocketChannel 用来监听网络读或者写操作。
  2. 通过 SelectionKey.interestOps(int ops) 方法可以方便地修改监听操作位。所以,此处注册需要获取 SelectionKey 并给 AbstractNIOChannel 的成员变量 selectionKey 赋值。

接下来继续看 pipeline.fireChannelRegistered()做了什么事情。

public final ChannelPipeline fireChannelRegistered() {
        AbstractChannelHandlerContext.invokeChannelRegistered(head);
        return this;
}

static void invokeChannelRegistered(final AbstractChannelHandlerContext next) {
       EventExecutor executor = next.executor();
      if (executor.inEventLoop()) {
          next.invokeChannelRegistered();
      } else {
          executor.execute(new Runnable() {
              @Override
              public void run() {
                  next.invokeChannelRegistered();
              }
          });
      }
}

private void invokeChannelRegistered() {
       if (invokeHandler()) {
           try {
               ((ChannelInboundHandler) handler()).channelRegistered(this);
           } catch (Throwable t) {
               notifyHandlerException(t);
           }
       } else {
           fireChannelRegistered();
       }
 }

在 invokeChannelRegistered() 会调用我们在前面设置的 handler (还记得签名的 handler(new LoggingServerHandler() )么)的 channelRegistered(),这个时候控制台应该会打印 loggin-channelRegistered。
到这里initAndRegister() (final ChannelFuture regFuture = initAndRegister();)就分析完毕了,该方法主要做如下三件事:

  1. 通过反射产生了一个 NioServerSocketChannle 对象;
  2. 调用 init(channel)完成初始化工作;
  3. 将NioServerSocketChannel进行了注册。
5.2 AbstractBootstrap.doBind ->AbstractBootstrap.doBind0()

下面分析 doBind0()到底做了些什么。源码如下

private static void doBind0(
         final ChannelFuture regFuture, final Channel channel,
         final SocketAddress localAddress, final ChannelPromise promise) {
     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());
             }
         }
     });
 }

首先new 一个线程 task,然后将该任务提交到 NioEventLoop 中进行处理,我们先看 execute()。

    @Override
    public void execute(Runnable task) {
        if (task == null) {
            throw new NullPointerException("task");
        }

        boolean inEventLoop = inEventLoop();
        if (inEventLoop) {
            addTask(task);
        } else {
            startThread();
            addTask(task);
            if (isShutdown() && removeTask(task)) {
                reject();
            }
        }

        if (!addTaskWakesUp && wakesUpForTask(task)) {
            wakeup(inEventLoop);
        }
    }

调用 inEventLoop() 判断当前线程是否为该 NioEventLoop 所关联的线程,如果是,则调用 addTask() 将任务 task 添加到队列中,如果不是,则先启动线程,在调用 addTask() 将任务 task 添加到队列中。addTask() 如下:

protected void addTask(Runnable task) {
    if (task == null) {
        throw new NullPointerException("task");
    }
    if (!offerTask(task)) {
        reject(task);
    }
}

final boolean offerTask(Runnable task) {
    if (isShutdown()) {
        reject();
    }
    return taskQueue.offer(task);
}

task 添加到任务队列 taskQueue成功后,执行任务会有如下的调用链:

channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);

public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
    return pipeline.bind(localAddress, promise);
}

public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
    return tail.bind(localAddress, promise);
}

tail 在 DefaultChannelPipeline 中定义:final AbstractChannelHandlerContext tail; 有 tail 就会有 head ,在 DefaultChannelPipeline 中维护这一个 AbstractChannelHandlerContext 节点的双向链表,该链表是实现 Pipeline 机制的关键,更多详情会在 ChannelPipeline 中做详细说明。bind() 最终会调用 DefaultChannelPipeline 的 bind() 方法。如下:

public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
    if (localAddress == null) {
        throw new NullPointerException("localAddress");
    }
    if (!validatePromise(promise, false)) {
        // cancelled
        return promise;
    }

    final AbstractChannelHandlerContext next = findContextOutbound();
    EventExecutor executor = next.executor();
    if (executor.inEventLoop()) {
        next.invokeBind(localAddress, promise);
    } else {
        safeExecute(executor, new Runnable() {
            @Override
            public void run() {
                next.invokeBind(localAddress, promise);
            }
        }, promise, null);
    }
    return promise;
}

首先对 localAddress 、 promise 进行校验,符合规范则调用 findContextOutbound() ,该方法用于在 pipeline 中获取 AbstractChannelHandlerContext 双向链表中的一个节点,如下:

private AbstractChannelHandlerContext findContextOutbound() {
    AbstractChannelHandlerContext ctx = this;
    do {
        ctx = ctx.prev;
    } while (!ctx.outbound);
    return ctx;
}

从该方法可以看出,所获取的节点是从 tail 开始遍历,获取第一个节点属性 outbound 为 true 的节点。其实该节点是 AbstractChannelHandlerContext 双向链表的 head 节点。获取该节点后,调用 invokeBind(),如下

private void invokeBind(SocketAddress localAddress, ChannelPromise promise) {
    if (invokeHandler()) {
        try {
            ((ChannelOutboundHandler) handler()).bind(this, localAddress, promise);
        } catch (Throwable t) {
            notifyOutboundHandlerException(t, promise);
        }
    } else {
        bind(localAddress, promise);
    }
}

这是因为HeadContext由于其继承AbstractChannelHandlerContext以及实现了ChannelHandler接口使其具有Context和Handler双重特性。然后调用其bind(),如下:

public void bind(
        ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
        throws Exception {
    unsafe.bind(localAddress, promise);
}

unsafe 定义在 HeadContext 中,在构造函数中初始化(unsafe = pipeline.channel().unsafe();),调用 bind() 如下:

public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
            assertEventLoop();
            ....
            boolean wasActive = isActive();
	        // 最核心方法
            doBind(localAddress);
            if (!wasActive && isActive()) {
                invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.fireChannelActive();
                    }
                });
            }

            safeSetSuccess(promise);
        }

内部调用 doBind() ,该方法为绑定中最核心的方法,位于 NioServerSocketChannel 中,如下

protected void doBind(SocketAddress localAddress) throws Exception {
    if (PlatformDependent.javaVersion() >= 7) {
        javaChannel().bind(localAddress, config.getBacklog());
    } else {
        javaChannel().socket().bind(localAddress, config.getBacklog());
    }
}

javaChannel()返回的是 NioServerSocketChannel 实例初始化时所产生的 Java NIO ServerSocketChannel 实例(ServerSocketChannelImple实例),然后调用其 bind(),在这里真正的开始监听端口。

其实在监听完成后,接着执行pipeline.fireChannelActive()这行代码,这里有个挑战:为什么能够进入if语句,而在注册时不能进入?答案是注册的时候只是想Selector注册Channel,并没有激活Channel(激活就是开始监听端口、Socket连接)下面分析pipeline.fireChannelActive方法,此方法的实现在DefaultChannelPipeline中,内部调用了HeadContext的channelActive:

 @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
    ctx.fireChannelActive();
    readIfIsAutoRead();
}

第一行是继续激活后面的handler,第二行的实现:

private void readIfIsAutoRead() {
   if (channel.config().isAutoRead()) {
       channel.read();
   }
}
   @Override
public Channel read() {
	pipeline.read();
	return this;
}
public final ChannelPipeline read() {
    tail.read();
    return this;
}

readIfIsAutoRead中激活了Channel Read事件,read事件从Tail节点流向head

public void read(ChannelHandlerContext ctx) {
   unsafe.beginRead();
}

beginRead的实现AbstractChannel.AbstractUnsafe中:

  @Override
 public final void beginRead() {
 assertEventLoop();

 if (!isActive()) {
     return;
 }
     doBeginRead();
 }

channel.nio.AbstractNioChannel#doBeginRead实现如下:

  protected void doBeginRead() throws Exception {
        // Channel.read() or ChannelHandlerContext.read() was called
        final SelectionKey selectionKey = this.selectionKey;
        if (!selectionKey.isValid()) {
            return;
        }

        readPending = true;

        final int interestOps = selectionKey.interestOps();
        if ((interestOps & readInterestOp) == 0) {
            selectionKey.interestOps(interestOps | readInterestOp);
        }
    }

在这里真正的把ACCEPT/READ事件注册给Selector
至此说完了 Server端的整体流程,后续会出客户端以及EventLoop相关的源码分析。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值