Netty 源码分析 —— 启动(一)之服务端(很长很长)


我准备战斗到最后,不是因为我勇敢,是我想见证一切。 --双雪涛《猎人》


Thinking

  1. 一个技术,为什么要用它,解决了那些问题?
  2. 如果不用会怎么样,有没有其它的解决方法?
  3. 对比其它的解决方案,为什么最终选择了这种,都有何利弊?
  4. 你觉得项目中还有那些地方可以用到,如果用了会带来那些问题?
  5. 这些问题你又如何去解决的呢?

本文基于Netty 4.1.45.Final-SNAPSHOT

从本开始正式对Netty源码下手。激动的心,颤抖的手。😊

1、概述

​ 简单的介绍一下netty。

Netty 是一款提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。

也就是说,Netty 是一个基于 NIO 的客户、服务器端编程框架。使用 Netty 可以确保你快速和简单地开发出一个网络应用,例如实现了某种协议的客户,服务端应用。Netty 相当简化和流线化了网络应用的编程开发过程,例如,TCP 和 UDP 的 socket 服务开发。

源自《百度百科》

在了解Netty源码之前推荐先了解Netty的基础架构,和核心组件,了解Netty中核心组件是如何配置使用的。这里推荐阅读《Netty In Action》第一章的第三小节,详细的介绍了Netty的组件和涉及。

​ 说会正题,在使用Netty时,首先接触的就是程序的启动。所以最先涉及到的就是Netty提供的启动服务io.netty.bootstrap.Bootstrapio.netty.bootstrap.ServerBootstrap,这两个启动器组件。位置如下

类位置.png

类图如下:

类图.png

  • 从上面的类关系图可以看出,两个启动类分别属于服务端与客户端。并且大部分的方法都是相同的。都继承自AbstractBootstrap。

2、Netty提供的ServerBootStrap 示例

example包下的io.netty.example.echo.EchoServer

/**
 * Echoes back any received data from a client.
 * 回显从客户端接受到的数据
 */
public final class EchoServer {

    static final boolean SSL = System.getProperty("ssl") != null; // 获取SSL验证
    static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));

    public static void main(String[] args) throws Exception {
        // Configure SSL. 配置SSL
        final SslContext sslCtx;
        if (SSL) {
            SelfSignedCertificate ssc = new SelfSignedCertificate();
            sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
        } else {
            sslCtx = null;
        }

        // Configure the server.
        // 创建两个EventLoopGroup对象 boosGroup 用于处理父类Handler workerGroup用于处理 childHandler
        /**
         * 创建的两个线程组:
         *  boos线程组:用于服务端接受客户端的连接
         *  worker线程组:用于进行客户端的SocketChannel的数据读写
         *
         *  这里提个疑问:
         *      为什么要使用两个线程组???
         *      // TODO
         */
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        // 创建EchoServerHandler 对象
        final EchoServerHandler serverHandler = new EchoServerHandler();
        try {
            // 创建 ServerBootstrap对象用于设置服务端的启动配置
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup) // 设置创建的工作EventLoopGroup
                    .channel(NioServerSocketChannel.class) // 设置要被实例化的为 NioServerSocketChannel 类
                    .option(ChannelOption.SO_BACKLOG, 100) // 设置 NioServerSocketChannel 的可选项
                    .handler(new LoggingHandler(LogLevel.INFO)) // 设置BossGroup使用的日志处理器
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline p = ch.pipeline(); //  设置连入服务端的 Client 的 SocketChannel 的处理器 ,用于自定义Handler处理器
                            if (sslCtx != null) {
                                p.addLast(sslCtx.newHandler(ch.alloc()));
                            }
                            //p.addLast(new LoggingHandler(LogLevel.INFO));
                            p.addLast(serverHandler);
                        }
                    });
            // Start the server.
            // 绑定端口,并同步等待成功,即启动服务端。
            ChannelFuture f = b.bind(PORT).sync(); // 设置为 阻塞等待。

            // Wait until the server socket is closed.
            // 监听服务端关闭,并阻塞等待
            f.channel().closeFuture().sync();
        } finally {
            // Shut down all event loops to terminate all threads.
            // 优雅关闭
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

3、AbstractBootstrap

​ 源码分析启动类的公共父类io.netty.bootstrap.AbstractBootstrap

所有源码注意都push 到Git上。

3.1、构造方法

/**
 * {@link AbstractBootstrap} is a helper class that makes it easy to bootstrap a {@link Channel}. It support
 * method-chaining to provide an easy way to configure the {@link AbstractBootstrap}.
 *
 * {@link AbstractBootstrap}是一个帮助程序类,可以很容易地启动{@link Channel}。
 * 它支持方法链,以提供一种简便的方法来配置{@link AbstractBootstrap}。
 *
 * <p>When not used in a {@link ServerBootstrap} context, the {@link #bind()} methods are useful for connectionless
 * transports such as datagram (UDP).</p>
 *
 * <p> {@link #bind()}方法在{@link ServerBootstrap}上下文中不使用时,
 * 对于诸如数据报(UDP)之类的无连接传输非常有用。</ p>
 */
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
    @SuppressWarnings("unchecked")
    static final Map.Entry<ChannelOption<?>, Object>[] EMPTY_OPTION_ARRAY = new Map.Entry[0];
    @SuppressWarnings("unchecked")
    static final Map.Entry<AttributeKey<?>, Object>[] EMPTY_ATTRIBUTE_ARRAY = new Map.Entry[0];

    /**
     * EventLoopGroup 对象
     */
    volatile EventLoopGroup group;
    /**
     * Channel 工厂 用于创建Channel 工厂。
     */
    @SuppressWarnings("deprecation")
    private volatile ChannelFactory<? extends C> channelFactory;
    /**
     * 本地地址
     */
    private volatile SocketAddress localAddress;
    /**
     *  可选项集合
     *      ConcurrentHashMap 保证
     */
    private final Map<ChannelOption<?>, Object> options = new ConcurrentHashMap<ChannelOption<?>, Object>();
    /**
     *  属性集合
     *   这里需要注意的是使用的集合是:ConcurrentHashMap
     */
    private final Map<AttributeKey<?>, Object> attrs = new ConcurrentHashMap<AttributeKey<?>, Object>();
    /**
     * 处理器
     */
    private volatile ChannelHandler handler;

    AbstractBootstrap() {
        // Disallow extending from a different package.禁止从其他程序包扩展。
    }

    AbstractBootstrap(AbstractBootstrap<B, C> bootstrap) {
        group = bootstrap.group;
        channelFactory = bootstrap.channelFactory;
        handler = bootstrap.handler;
        localAddress = bootstrap.localAddress;
        options.putAll(bootstrap.options);
        attrs.putAll(bootstrap.attrs);
    }
  • AbstractBootstrap是个抽象类,并且实现Cloneable接口。另外声明B``C两个泛型。
    • B:继承AbstractBootstrap类,用于表示自身的类型。
    • C:继承Channel类,表示创建的Channel类型。
  • 上述代码需要注意的是,两个集合都是使用的并发包下的ConcurrentHashMap。在Netty追求性能的情况下,为什么要使用这个。(可能这两个属性集合存在多线程修改的情况。为了数据安全,使用并发包下的集合类。)

3.2、group

​ 设置EventLoopGroup到group中去

    /**
     * 设置 EventLoopGroup 到group中
     * The {@link EventLoopGroup} which is used to handle all the events for the to-be-created
     * {@link Channel}
     * {@link EventLoopGroup},用于处理要创建的所有事件{@link Channel}
     */
    public B group(EventLoopGroup group) {
        ObjectUtil.checkNotNull(group, "group");
        if (this.group != null) {
            throw new IllegalStateException("group set already");
        }
        this.group = group;
        return self();
    }

3.3、self

#self方法,返回自己。

    /**
     *  这里的B 就是类上声明的 B 泛型
     * @return 返回自身对象(自己)
     */
    @SuppressWarnings("unchecked")
    private B self() {
        return (B) this;
    }

3.4、channel

​ 设置要被实例化的Channel类

    /**
     * 设置要被实例化的Channel类
     *
     * The {@link Class} which is used to create {@link Channel} instances from.
     * You either use this or {@link #channelFactory(io.netty.channel.ChannelFactory)} if your
     * {@link Channel} implementation has no no-args constructor.
     *
     *  {@link Class},用于从中创建{@link Channel}实例。
     *  如果您的{@link Channel}实现没有no-args构造函数,
     *  则可以使用this或{@link #channelFactory(io.netty.channel.ChannelFactory)}
     */
    public B channel(Class<? extends C> channelClass) {
        return channelFactory(new ReflectiveChannelFactory<C>(
                ObjectUtil.checkNotNull(channelClass, "channelClass")
        ));
    }
  • 这里涉及到的io.netty.channel.ReflectiveChannelFactory#ReflectiveChannelFactory,使用反射机制实例化Channel

3.4.1、io.netty.channel.ReflectiveChannelFactory

/**
 * A {@link ChannelFactory} that instantiates a new {@link Channel} by invoking its default constructor reflectively.
 * 使用默认的构造方法 利用反射创建ChannelFactory实例。
 */
public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {

    /**
     * 成员变量为 构造器
     */
    private final Constructor<? extends T> constructor;

    /**
     * 使用 clazz.getConstructor()获取构造器
     * @param clazz Channel类的class对象
     */
    public ReflectiveChannelFactory(Class<? extends T> clazz) {
        ObjectUtil.checkNotNull(clazz, "clazz");
        try {
            this.constructor = clazz.getConstructor();
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException("Class " + StringUtil.simpleClassName(clazz) +
                    " does not have a public non-arg constructor", e);
        }
    }

    /**
     *  使用构造器反射 实例化Channel
     * @return Channel实例
     */
    @Override
    public T newChannel() {
        try {
            // 反射调用构造器方法
            return constructor.newInstance();
        } catch (Throwable t) {
            throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);
        }
    }

3.5、channelFactory

    /**
     * 建议使用channel包下的 ChannelFactory
     * @deprecated Use {@link #channelFactory(io.netty.channel.ChannelFactory)} instead.
     */
    @Deprecated
    public B channelFactory(ChannelFactory<? extends C> channelFactory) {
        // 检查是否为空,   为空throw new NullPointerException(text);
        ObjectUtil.checkNotNull(channelFactory, "channelFactory");
        if (this.channelFactory != null) { // 不允许重复设置
            throw new IllegalStateException("channelFactory set already");
        }

        this.channelFactory = channelFactory;
        return self();
    }

    @SuppressWarnings({ "unchecked", "deprecation" })
    public B channelFactory(io.netty.channel.ChannelFactory<? extends C> channelFactory) {
        return channelFactory((ChannelFactory<C>) channelFactory);
    }

这里建议使用io.netty.channel.ChannelFactory

Channel 工厂接口,用于创建 Channel 对象。代码如下

@SuppressWarnings({ "ClassNameSameAsAncestorName", "deprecation" })
public interface ChannelFactory<T extends Channel> extends io.netty.bootstrap.ChannelFactory<T> {
    /**
     * Creates a new channel.
     * 返回的是 T 泛型 , 为channel 对象。
     */
    @Override
    T newChannel(); // 创建Channel对象
}

3.6、localAddress

​ 用于绑定Channel 本地地址,该方法有四个重载方法

  /**
     * 用于绑定Channel 本地地址 有四个重载方法。
     * The {@link SocketAddress} which is used to bind the local "end" to.
     */
    public B localAddress(SocketAddress localAddress) {
        this.localAddress = localAddress;
        return self();
    }

    /**
     * @see #localAddress(SocketAddress)
     */
    public B localAddress(int inetPort) {
        return localAddress(new InetSocketAddress(inetPort));
    }

    /**
     * @see #localAddress(SocketAddress)
     */
    public B localAddress(String inetHost, int inetPort) {
        return localAddress(SocketUtils.socketAddress(inetHost, inetPort));
    }

    /**
     * @see #localAddress(SocketAddress)
     */
    public B localAddress(InetAddress inetHost, int inetPort) {
        return localAddress(new InetSocketAddress(inetHost, inetPort));
    }

其他的源码简单的请移步Git

3.。。。。。。其它简单的源代码请移步Git

3.10 config

io.netty.bootstrap.AbstractBootstrap#config,获取当前AbstractBootstrap 的配置对象

    /**
     * 获取 当前 AbstractBootstrap  的配置对象
     * Returns the {@link AbstractBootstrapConfig} object that can be used to obtain the current config
     * of the bootstrap.
     * 返回{@link AbstractBootstrapConfig}对象,该对象可用于获取bootstrap的当前配置
     */
    public abstract AbstractBootstrapConfig<B, C> config();
  • 这是一个内部类,并且是抽象的。看一下它的子类。子类.png

  • 从类图可以看出,Config类与bootstrap类一样,分客户端与服务端两种,但是都是继承自同一个父类,所以大致思路都差不多。下面就进去看一下有哪些不同。

3.10.1ServerBootstrapConfig

io.netty.bootstrap.ServerBootstrapConfig。服务端的公开配置类

/**
 * Exposes the configuration of a {@link ServerBootstrapConfig}.
 * 公开{@link ServerBootstrapConfig}的配置。服务端
 * 服务端有两个工作线程组。所以存在 父子关系。一般worker线程都是要加载child。
 */
public final class ServerBootstrapConfig extends AbstractBootstrapConfig<ServerBootstrap, ServerChannel> {

    ServerBootstrapConfig(ServerBootstrap bootstrap) {
        super(bootstrap);
    }

    /**
     * Returns the configured {@link EventLoopGroup} which will be used for the child channels or {@code null}
     * if non is configured yet.
     * 返回已配置的{@link EventLoopGroup},它将用于子通道;如果尚未配置,则返回{@code null}
     */
    @SuppressWarnings("deprecation")
    public EventLoopGroup childGroup() {
        return bootstrap.childGroup();
    }

    /**
     * Returns the configured {@link ChannelHandler} be used for the child channels or {@code null}
     * if non is configured yet.
     * 返回已配置{@link ChannelHandler},它将用于子通道;如果未配置,则返回{@code null}
     */
    public ChannelHandler childHandler() {
        return bootstrap.childHandler();
    }

    /**
     * Returns a copy of the configured options which will be used for the child channels.
     * 返回将用于子通道的已配置选项的副本。
     * 返回的对象为一个新的map集合。
     */
    public Map<ChannelOption<?>, Object> childOptions() {
        return bootstrap.childOptions();
    }

    /**
     * Returns a copy of the configured attributes which will be used for the child channels.
     * 返回将用于子通道的已配置属性的副本。
     * 同上一样
     */
    public Map<AttributeKey<?>, Object> childAttrs() {
        return bootstrap.childAttrs();
    }

  • 服务端的配置类,多是针对子通道

3.10.2 BootstrapConfig`

io.netty.bootstrap.BootstrapConfig

/**
 * Exposes the configuration of a {@link Bootstrap}.
 * 公开{@link Bootstrap}配置
 */
public final class BootstrapConfig extends AbstractBootstrapConfig<Bootstrap, Channel> {

    BootstrapConfig(Bootstrap bootstrap) {
        super(bootstrap);
    }

    /**
     * Returns the configured remote address or {@code null} if non is configured yet.
     * 返回已配置的远程地址;如果尚未配置,则返回{@code null}。
     */
    public SocketAddress remoteAddress() {
        return bootstrap.remoteAddress();
    }

    /**
     * Returns the configured {@link AddressResolverGroup} or the default if non is configured yet.
     * 返回已配置的{@link AddressResolverGroup};如果尚未配置,则返回{@code null}。
     */
    public AddressResolverGroup<?> resolver() {
        return bootstrap.resolver();
    }

3.11 bind

io.netty.bootstrap.AbstractBootstrap#bind()。绑定端口,启动服务端。

    /**
     * Create a new {@link Channel} and bind it.
     * 创建一个新的{@link Channel},并将其绑定。
     * 与{@link #localAddress(SocketAddress)}对应,有四个重载方法
     * 该方法返回的{@link ChannelFuture}对象。也就是异步的绑定端口,启动服务端。
     * 如果需要同步,则需要调用 {@link ChannelFuture#sync()} 方法。该方法会阻塞。
     */
    public ChannelFuture bind() {
        validate(); // 校验服务启动需要的所有配置
        SocketAddress localAddress = this.localAddress;
        if (localAddress == null) {
            throw new IllegalStateException("localAddress not set");
        }
        // 绑定本地地址(包括端口)
        return doBind(localAddress);
    }

    /**
     * Create a new {@link Channel} and bind it.
     */
    public ChannelFuture bind(int inetPort) {
        return bind(new InetSocketAddress(inetPort));
    }

    /**
     * Create a new {@link Channel} and bind it.
     */
    public ChannelFuture bind(String inetHost, int inetPort) {
        return bind(SocketUtils.socketAddress(inetHost, inetPort));
    }

    /**
     * Create a new {@link Channel} and bind it.
     */
    public ChannelFuture bind(InetAddress inetHost, int inetPort) {
        return bind(new InetSocketAddress(inetHost, inetPort));
    }

    /**
     * Create a new {@link Channel} and bind it.
     */
    public ChannelFuture bind(SocketAddress localAddress) {
        validate();
        // 使用 自定义localAddress,如果不为空。
        return doBind(ObjectUtil.checkNotNull(localAddress, "localAddress"));
    }
  • 与{@link #localAddress(SocketAddress)}对应,有四个重载方法。
  • 该方法返回的是 ChannelFuture 对象,也就是异步的绑定端口,启动服务端。如果需要同步,则需要调用 ChannelFuture#sync() 方法。
  • 核心流程.png

3.11.1、doBind

io.netty.bootstrap.AbstractBootstrap#doBind里涉及到大量的JDK Future异步操作,所以我们先简单的看一下Netty是则呢封装JDK 并发包下的Future的。

3.11.1.1、Netty中怎么封装Future的

​ Netty中与JDK Future 的类关系图

与JDK Future关系图.png

我们先看一下io.netty.util.concurrent.Future,是如何继承java.util.concurrent.Future的。

这里首先提出一个疑问,在JDK已经提供了Future的实现,用于执行异步操作并且提供了相应对结果操作的方法。那为什么Netty内部还要维护一个自己的Future类,并且继承了JDK的Future接口。

  1. 我们首先看一下JDK中的Futrue

该接口表示的是异步计算的结果,提供若干方法来检测计算是否完成、等待计算完成、获取计算的结果。

这种形式出现的解决的痛点就是在多线程并发开发下,Runable 执行的程序并没有返回值,很多程序的运行状态开发人员并不能很清楚的知晓。所以在很多并发条件下,并不能更加丰富且真实的完成并发。

eg:java.util.concurrent.Future源码中提供的示例

* A {@code Future} represents the result of an asynchronous
 * computation.  Methods are provided to check if the computation is
 * complete, to wait for its completion, and to retrieve the result of
 * the computation.  The result can only be retrieved using method
 * {@code get} when the computation has completed, blocking if
 * necessary until it is ready.  Cancellation is performed by the
 * {@code cancel} method.  Additional methods are provided to
 * determine if the task completed normally or was cancelled. Once a
 * computation has completed, the computation cannot be cancelled.
 * If you would like to use a {@code Future} for the sake
 * of cancellability but not provide a usable result, you can
 * declare types of the form {@code Future<?>} and
 * return {@code null} as a result of the underlying task.
     {@code Future}表示异步计算的结果。提供了一些方法来检查计算是否完成,等待其完成并检索计算结果。
     计算完成后,只能使用方法 {@code get}来检索结果;如果需要,则阻塞直到准备好为止。
     取消是通过 {@code cancel}方法执行的。
     提供了其他方法来确定任务是正常完成还是被取消。 
     计算完成后,无法取消计算。 
     如果出于可取消性的原因而想使用{@code Future},但不提供可用的结果,则可以声明{@code Future <>}形式的类型,并返回{@code null}作为基础任务的结果。
 *
 * <p>
 * <b>Sample Usage</b> (Note that the following classes are all
 * made-up.)
 * <pre> {@code 简单的示例
 * interface ArchiveSearcher { String search(String target); }
 * class App {
 *   ExecutorService executor = ...
 *   ArchiveSearcher searcher = ...
 *   void showSearch(final String target)
 *       throws InterruptedException {
 *     Future<String> future
 *       = executor.submit(new Callable<String>() {
 *         public String call() {
 *             return searcher.search(target);
 *         }});
 *     displayOtherThings(); // do other things while searching
 *     try {
 *       displayText(future.get()); // use future
 *     } catch (ExecutionException ex) { cleanup(); return; }
 *   }
 * }}</pre>
 *
 * The {@link FutureTask} class is an implementation of {@code Future} that
 * implements {@code Runnable}, and so may be executed by an {@code Executor}.
 * For example, the above construction with {@code submit} could be replaced by:
 *   {@link FutureTask}类是{@code Future}的实现,它实现了{@code Runnable},因此可以由{@code Executor}执行。 
     例如,上面的{@code Submit}结构可以替换为
     <pre> {@code
 * FutureTask<String> future =
 *   new FutureTask<String>(new Callable<String>() {
 *     public String call() {
 *       return searcher.search(target);
 *   }});
 * executor.execute(future);}</pre>
 *
 * <p>Memory consistency effects: Actions taken by the asynchronous computation
 * <a href="package-summary.html#MemoryVisibility"> <i>happen-before</i></a>
 * actions following the corresponding {@code Future.get()} in another thread.
     
     受内存一致性的影响。Future.get() 会阻塞线程。
 *
  1. Netty中的Future

    io.netty.util.concurrent.Future继承JDK Future

    public interface Future<V> extends java.util.concurrent.Future<V> {
    

    netty基于JDK的Future更加强化了它的功能性。下面列举几个比较重要的例子

    • io.netty.util.concurrent.Future#cause:表示I/O操作失败时,返回异常信息。

    • io.netty.util.concurrent.Future#cancel:使用布尔值,用于对以及该开始执行的Future进行中断操作。

    • io.netty.util.concurrent.Future#isSuccess:仅当I / O操作成功完成时,才返回{@code true}

      • 对于上述jdk中Future申明的isDone方法,只能知道I/O是否结束有可能是成功完成、被取消、异常中断。netty中Future的此isSuccess方法能够很好的判断出操作是否真正地成功完成

    • io.netty.util.concurrent.Future#sync:等待这个future直到完成,如果这个future失败,则重新抛出失败原因。该方法会一直阻塞。(所以在Netty 服务端的示例中,在绑定端口号时,会调用该方法,使其处于等待阻塞状态,取消异步处理)

    • io.netty.util.concurrent.Future#addListener:将指定的侦听器添加到当前future。当此future完成时{ linkplain #isDone()完成}(返回true时),将通知指定的侦听器。如果这个future已经完成,则立即通知指定的侦听器

      • 这是Netty 对JDK Future扩展中最重要的一个方法,这里使用了观察者模式

      • /**
         * Listens to the result of a {@link Future}.  The result of the asynchronous operation is notified once this listener
         * is added by calling {@link Future#addListener(GenericFutureListener)}.
         * 监听{@link Future}的结果。一旦通过调用{@link Future#addListener(GenericFutureListener)}添加了此侦听器,
         * 便会通知异步操作的结果。
         */
        public interface GenericFutureListener<F extends Future<?>> extends EventListener {
        
            /**
             * Invoked when the operation associated with the {@link Future} has been completed.
             *
             * 与{@link Future}关联的操作完成时调用。
             * @param future  the source {@link Future} which called this callback 回调
             */
            void operationComplete(F future) throws Exception;
        }
        
        • 为什么JDK Future中已经提供了java.util.concurrent.Future#get()方法来获取异步的结果,Netty还要提供观察者模式用于获取异步结果呢?

          ​ 答:原因:

          1. java.util.concurrent.Future#get()获取操作会一直阻塞,直到Future执行完毕。违背了异步调用的原则。
          2. 我们在使用java.util.concurrent.Future#get()会考虑到底什么时候使用,因为该方法会阻塞后续的逻辑代码,如果我们使用监听器,会更加优雅地决定在合理的时间来处理我们的逻辑代码(监听器会监听整个过程,完成后会立即通知指定地侦听器)
    • io.netty.util.concurrent.Future#getNow:返回这个结果并且不会阻塞,如果当前future并没有完成则将会返回{@code null}.

      • 这个方法专门针对 java.util.concurrent.Future#get() 会阻塞后续代码提出的。但是有一个问题如果当前future没有完成返回 null,但是有可能Future在完成后返回的结果就是null呢?所以这里有个点需要注意:

        • 由于可能使用{@code null}值将future标记为成功,(有可能当前的future 返回的结果就是null ,如果单一的凭借这种标志来决定future是否完成太过草率了)
          * 因此您还需要检查future是否真的由{@link #isDone()}完成而不依赖于返回的{@code null}值。
          
          
        • 检查 isDone()是否返回true;

  2. ChannelFuture

image-20200216225838070.png

netty中的ChannelFuture继承来netty中的自己的Future

addListener方法传入的监听器会实现以下接口,也就是被通知的时候operationComplete方法会被调用:

public interface ChannelFuture extends Future<Void>

ChannelFuture表示Channel中异步I/O操作的结果,在netty中所有的I/O操作都是异步的,I/O的调用会直接返回,可以通过ChannelFuture来获取I/O操作的结果状态。对于多种状态的表示如下:

ChannelFuturedoc 文档中强调了几点:因为文档太长了,可以到我的git上去查看。

 *                                      | Completed successfully    |
 *                                      +---------------------------+
 *                                 +---->      isDone() = true      |
 * +--------------------------+    |    |   isSuccess() = true      |
 * |        Uncompleted       |    |    +===========================+
 * +--------------------------+    |    | Completed with failure    |
 * |      isDone() = false    |    |    +---------------------------+
 * |   isSuccess() = false    |----+---->      isDone() = true      |
 * | isCancelled() = false    |    |    |       cause() = non-null  |
 * |       cause() = null     |    |    +===========================+
 * +--------------------------+    |    | Completed by cancellation |
 *                                 |    +---------------------------+
 *                                 +---->      isDone() = true      |
 *                                      | isCancelled() = true      |
 *                                      +---------------------------+

文档中强调了一点,一个ChannelFuture无非只有两种状态,完成与未完成。但是在完成状态下包含了三种情况都视为已经完成:

  1. successfully 成功
  2. failure 失败
  3. cancellation 取消

以上三种情况都为 完成状态。

ChannelFuture中需要注意的是添加了channel方法来获取Channel:

 /**
     * Returns a channel where the I/O operation associated with this
     * future takes place.
     */
    Channel channel();
JDK所提供的Future只能通过手工方式检查执行结果,而这个操作是会阻塞的;Netty则对ChannelFuture进行来增强,通过ChannelFutureListener以回调的方式来获取执行结果,
去除来手工检查阻塞的操作。需要注意的是ChannelFutureListener的operationComplete方法是由I/O线程执行的,因此要注意的是不要在这里执行耗时操作,否则需要通过另外的线程或线程池来执行
  1. ChannelPromise

    ChannelPromise是一种可写的特殊ChannelFuture。

ChannelPromise.png

/**
 * Special {@link ChannelFuture} which is writable.
 * 一个特殊的{@link ChannelFuture}
 *  它是可写的。
 */
public interface ChannelPromise extends ChannelFuture, Promise<Void> 
  • 并且属性的设置只能设置一次。(具体源码解析见后续文章。这里已经严重跑题了 😊)。

接下来我们接着研究io.netty.bootstrap.AbstractBootstrap#doBind,因为这里涉及到了ChannelFuture的异步操作所以带入了一点Future的东西。

 private ChannelFuture doBind(final SocketAddress localAddress) {
        // 初始化并注册一个 Channel 对象,因为注册是异步的过程,所以返回一个 ChannelFuture 对象。
        final ChannelFuture regFuture = initAndRegister(); // 初始化并且注册
        final Channel channel = regFuture.channel(); // ChannelFuture 会利用监听器回调返回一个Channel
        if (regFuture.cause() != null) { // 若发生异常,直接返回,该ChannelFuture对象会携带异常信息
            return regFuture;
        }
        // 绑定Channel 端口,并且注册 Channel 到 SelectionKey 中。
        if (regFuture.isDone()) { // 如果此任务完成
            // At this point we know that the registration was complete and successful.
            ChannelPromise promise = channel.newPromise();
            doBind0(regFuture, channel, localAddress, promise); // 绑定。
            return promise;
        } else {
            // Registration future is almost always fulfilled already, but just in case it's not.
            //未来的注册几乎总是已经实现,但以防万一还没有完成。
            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) {
                        // Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
                        // IllegalStateException once we try to access the EventLoop of the Channel.
                        promise.setFailure(cause);
                    } else {
                        // Registration was successful, so set the correct executor to use.
                        // 注册成功,因此请设置要使用的正确执行器
                        // See https://github.com/netty/netty/issues/2586
                        promise.registered();

                        doBind0(regFuture, channel, localAddress, promise);
                    }
                }
            });
            return promise;
        }
    }
  • 第3行:final ChannelFuture regFuture = initAndRegister(); 初始化并且注册Channel,返回一个异步的ChannelFuture 对象

  • 从9-14再14-35分为两个部分,因为Future无非是两种情况,完成与未完成。

    • #isDone:执行doBind0(regFuture, channel, localAddress, promise);绑定 Channel 的端口,并注册 Channel 到 SelectionKey 中。

    • elsefinal PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);创建一个可写的ChannelFuture,并且将它添加到当前ChannelFuture中。

      • 然后判断该Future中是否失败。

      • elese:

        //添加监听器,在注册完成后,进行回调执行 #doBind0(...) 方法的逻辑
        promise.registered();
        
        doBind0(regFuture, channel, localAddress, promise);
        
  • 所以可以看出,bind的逻辑是再执行register的逻辑之后。

3.11.2、initAndRegister

io.netty.bootstrap.AbstractBootstrap#initAndRegister

    final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            channel = channelFactory.newChannel(); // 使用工厂类创建channel
            init(channel); // 初始化Channel 配置,具体在子类中实现
        } catch (Throwable t) { // 发生异常
            if (channel != null) { // channel已经创建
                // channel can be null if newChannel crashed (eg SocketException("too many open files"))
                channel.unsafe().closeForcibly(); // 强制关闭channel
                // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
                // 返回一个带异常的 DefaultChannelPromise
                return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
        }
            // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
            // 由于尚未注册频道,因此我们需要强制使用GlobalEventExecutor
            // 因为创建Channel对象失败,所示使用new FailedChannel()
            return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
        }

        //首先获得 EventLoopGroup 对象,后调用 EventLoopGroup#register(Channel) 方法,
        // 注册 Channel 到 EventLoopGroup 中。实际在方法内部,
        // EventLoopGroup 会分配一个 EventLoop 对象,将 Channel 注册到其上,并通知ChannelFuture
        ChannelFuture regFuture = config().group().register(channel);
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) { // Channel 注册成功,正常关闭。
                channel.close(); 
            } else {
                channel.unsafe().closeForcibly();
            }
        }

        // If we are here and the promise is not failed, it's one of the following cases:
        // 1) If we attempted registration from the event loop, the registration has been completed at this point.
        //    i.e. It's safe to attempt bind() or connect() now because the channel has been registered.
        // 2) If we attempted registration from the other thread, the registration request has been successfully
        //    added to the event loop's task queue for later execution.
        //    i.e. It's safe to attempt bind() or connect() now:
        //         because bind() or connect() will be executed *after* the scheduled registration task is executed
        //         because register(), bind(), and connect() are all bound to the same thread.
        //  如果我们在这里并且promise没有失败,则是以下情况之一:
        //  1)如果我们尝试从事件循环中进行注册,则注册已完成。
        //      即立即尝试bind()或connect()是安全的,因为该通道已被注册。
        //  2)如果我们尝试从另一个线程进行注册,则注册请求已成功
        //      添加到事件循环的任务队列中,以便以后执行。
        //      立即尝试bind()或connect()是安全的:
        //          因为bind()或connect()将在*计划的注册任务执行后*执行,
        //          因为register(),bind()和connect()都绑定到同一线程。

        return regFuture;
    }
  • 23行:注册 Channel 到 EventLoopGroup 中,并且通知ChannelFuture注册完成。返回ChannleFuture,有可能注册成功但是出现错误,有可能注册都没有成功,

    • 所以分了正常关闭channle channel.close(); 和强制关闭channel.unsafe().closeForcibly();

    • 我们看一下这两种关闭方式的区别

    • /**
       * Close the {@link Channel} of the {@link ChannelPromise} and notify the {@link ChannelPromise} once the
       * operation was complete.
       *
       * 操作完成后,关闭{@link ChannelPromise}的{@link Channel},并通知{@link ChannelPromise}。
       */
      void close(ChannelPromise promise);
      
      /**
       * Closes the {@link Channel} immediately without firing any events.  Probably only useful
       * when registration attempt failed.
       */
      void closeForcibly();
      
  • 正常的close 关闭 会触发事件,通知ChannelPromise。

  • 强制关闭,不会触发任何事件。

3.11.3、创建Channel对象

channel = channelFactory.newChannel();

上述代码中使用,反射机制实现Channel 的创建。

​ 以NioServerSocketChannel的创建过程为例。流程图如下:

创建 NioServerSocketChannel 对象.png

官方给与NettyChannel 的定义

A nexus to a network socket or a component which is capable of I/O operations such as read, write, connect, and bind

下面我们来看一下NioServerSocketChannel具体的实现源码

/**
 * A {@link io.netty.channel.socket.ServerSocketChannel} implementation which uses
 * NIO selector based implementation to accept new connections.
 * 基于 NIO selector的实现来接受连接的一个{@link io.netty.channel.socket.ServerSocketChannel}的实现
 */
public class NioServerSocketChannel extends AbstractNioMessageChannel
                             implements io.netty.channel.socket.ServerSocketChannel {
    private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
    private static ServerSocketChannel newSocket(SelectorProvider provider) {
        try {
            /**
             *  Use the {@link SelectorProvider} to open {@link SocketChannel} and so remove condition in
             *  {@link SelectorProvider#provider()} which is called by each ServerSocketChannel.open() otherwise.
             *
             *  See <a href="https://github.com/netty/netty/issues/2308">#2308</a>.
             */
            return provider.openServerSocketChannel(); // 多么熟悉的味道啊。是不是感觉特别亲切?😊
        } catch (IOException e) {
            throw new ChannelException(
                    "Failed to open a server socket.", e);
        }
    }

    private final ServerSocketChannelConfig config;

    /**
     * Create a new instance
     */
    public NioServerSocketChannel() {
        this(newSocket(DEFAULT_SELECTOR_PROVIDER));
    }

    /**
     * Create a new instance using the given {@link SelectorProvider}.
     */
    public NioServerSocketChannel(SelectorProvider provider) {
        this(newSocket(provider));
    }

    /**
     * Create a new instance using the given {@link ServerSocketChannel}.
     */
    public NioServerSocketChannel(ServerSocketChannel channel) {
        super(null, channel, SelectionKey.OP_ACCEPT);
        config = new NioServerSocketChannelConfig(this, javaChannel().socket());
    }
    
      private final ServerSocketChannelConfig config;

  • DEFAULT_SELECTOR_PROVIDER默认的SelectorProvider实现类。

  • config,每个Channel对应的配置对象,每种Channel实现类都会又对应的ChannelConfig实现类。例如:NioServerSocketChannel对应的就是ServerSocketChannelConfig

  • io.netty.channel.socket.nio.NioServerSocketChannel中的构造都依赖于静态方法io.netty.channel.socket.nio.NioServerSocketChannel#newSocket

  • 构造函数中还可以接受Channel 对象。研究一下这个独特的构造函数。

    public NioServerSocketChannel(ServerSocketChannel channel) {
        super(null, channel, SelectionKey.OP_ACCEPT);
        config = new NioServerSocketChannelConfig(this, javaChannel().socket());
    }
    
  • 调用了父类构造器。

3.11.3.1、AbstractNioMessageChannel

io.netty.channel.nio.AbstractNioMessageChannel

    /**
     * @see AbstractNioChannel#AbstractNioChannel(Channel, SelectableChannel, int)
     */
    protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent, ch, readInterestOp);
    }

直接调用了父类构造。

3.11.3.2、AbstractNioChannel
    protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent);
        this.ch = ch;
        this.readInterestOp = readInterestOp;
        try {
            ch.configureBlocking(false); // 通道设置为非阻塞状态,是不是熟悉的味道
        } catch (IOException e) {
            try {
                ch.close();
            } catch (IOException e2) {
                logger.warn(
                            "Failed to close a partially initialized socket.", e2);
            }

            throw new ChannelException("Failed to enter non-blocking mode.", e);
        }
    }
  • ch属性为:io.netty.channel.Channel,是Neety Channel ,持有JDK Channel
  • 调用 父类构造。
  • ch.configureBlocking(false);:多么熟悉亲切的代码😊
3.11.3.3、 AbstractChannel
    /**
     * Creates a new instance.
     *
     * @param parent
     *        the parent of this channel. {@code null} if there's no parent.
     */
    protected AbstractChannel(Channel parent) { 
        this.parent = parent;
        id = newId();
        unsafe = newUnsafe();
        pipeline = newChannelPipeline();
    }
  • 一顿赋值操作。
  • parent:为父Channel对象,如果没有父类,则为NULL。
  • 这一步才是真正的创建完Channel,并且设置ChannleId等属性。
  • id 属性,Channel 编号对象。在构造方法中,通过调用 #newId() 方法,进行创建。
  • unsafe 属性,Unsafe 对象。在构造方法中,通过调用 #newUnsafe() 方法,进行创建。
    • 这里的 Unsafe 并不是我们常说的 Java 自带的sun.misc.Unsafe ,而是 io.netty.channel.Channel#Unsafe
  • pipeline 属性,DefaultChannelPipeline 对象。在构造方法中,通过调用 #newChannelPipeline() 方法,进行创建。本文就先不分享,感兴趣的胖友自己看。
3.11.3.4 、小小的总结

​ 在整个创建Channel的过程中,对于一个Netty NIO Channel对象,它包含了如下几个核心组件:

  1. ChannelId
  2. Unsafe
  3. Pipeline
    1. ChannleHandler
  4. ChannelConfig
  5. Java 原生 NIO Channel

3.11.4、初始化Channel配置

init(channel)是一个抽象方法,具体实现由对应的类。
image-20200217213017061.png

3.11.5、 注册Channel到EventLoopGroup中

io.netty.channel.EventLoopGroup#register(io.netty.channel.Channel)方法,注册Channel到 EventLoopGroup 中。整体流程如下:

register 流程.png

我们直接从第4步入手,这里涉及到的操作都是基于io.netty.channel.AbstractChannel.AbstractUnsafe这个内部类。继承了io.netty.channel.Channel.Unsafe

@Override
        public final void register(EventLoop eventLoop, final ChannelPromise promise) {
            ObjectUtil.checkNotNull(eventLoop, "eventLoop"); // 校验非空
            if (isRegistered()) { // 校验未注册
                promise.setFailure(new IllegalStateException("registered to an event loop already"));
                return;
            }
            if (!isCompatible(eventLoop)) { // 校验Channel和EventLoop匹配
                promise.setFailure(
                        new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
                return;
            }

            AbstractChannel.this.eventLoop = eventLoop;

            if (eventLoop.inEventLoop()) { // 判断是否为当前线程。要保证在eventLoop线程组中执行注册逻辑
                register0(promise);
            } else {
                try {
                    // 使用eventLoop 线程组中的线程实现注册操作
                    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);
                }
            }
        }

        private void register0(ChannelPromise promise) {
            try {
                // check if the channel is still open as it could be closed in the mean time when the register
                // call was outside of the eventLoop
                // 检查通道是否仍处于打开状态,因为在注册调用在eventLoop外部的同时可能会关闭
                if (!promise.setUncancellable() || !ensureOpen(promise)) {
                    return;
                }
                boolean firstRegistration = neverRegistered; // 记录是否为首次注册
                doRegister(); // 执行注册逻辑
                neverRegistered = false; // 修改标记,说明应该不是首次注册。
                registered = true; // 维护Channel 已经注册

                // Ensure we call handlerAdded(...) before we actually notify the promise. This is needed as the
                // user may already fire events through the pipeline in the ChannelFutureListener.
                // //确保我们在实际通知promise之前调用handlerAdded(...)。
                // 这是必需的,因为用户可能已经通过ChannelFutureListener中的pipeline触发了事件
                pipeline.invokeHandlerAddedIfNeeded();

                // 回调通知已注册事件
                safeSetSuccess(promise);
                pipeline.fireChannelRegistered(); // 触发通道已注册事件
                // Only fire a channelActive if the channel has never been registered. This prevents firing
                // multiple channel actives if the channel is deregistered and re-registered.
                // 仅当从未注册过频道时才触发channelActive。
                // 如果取消注销并重新注册了通道,则可以防止激活多个通道。
                if (isActive()) {
                    if (firstRegistration) {
                        pipeline.fireChannelActive();
                    } else if (config().isAutoRead()) {
                        // This channel was registered before and autoRead() is set. This means we need to begin read
                        // again so that we process inbound data.
                        //该通道已注册,并已设置autoRead()。这意味着我们需要再次开始read,以便处理入站数据
                        // See https://github.com/netty/netty/issues/4805
                        beginRead();
                    }
                }
            } catch (Throwable t) {
                // Close the channel directly to avoid FD leak.
                closeForcibly();
                closeFuture.setClosed();
                safeSetFailure(promise, t);
            }
        }
  • 配合Debug与上面的流程图食用效果更佳哦!😊
  1. 注册方式是多态的,它既可以被 NIOServerSocketChannel 用来监听客户端的连接接入,也可以注册 SocketChannel 用来监听网络读或者写操作。

  2. 通过

    SelectionKey#interestOps(int ops)
    

    方法可以方便地修改监听操作位。所以,此处注册需要获取 SelectionKey 并给 AbstractNIOChannel 的成员变量selectionKey赋值。

    • 如果不理解,没关系,在下文中,我们会看到服务端对 SelectionKey.OP_ACCEPT 事件的关注。

Channel注册完之后,调用doBind0

3.11.6 doBind0

#doBind0(...) 方法,执行 Channel 的端口绑定逻辑。代码如下:

 1: private static void doBind0(
 2:         final ChannelFuture regFuture, final Channel channel,
 3:         final SocketAddress localAddress, final ChannelPromise promise) {
 4: 
 5:     // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up
 6:     // the pipeline in its channelRegistered() implementation.
 7:     channel.eventLoop().execute(new Runnable() {
 8:         @Override
 9:         public void run() {
11:             // 注册成功,绑定端口
12:             if (regFuture.isSuccess()) {
13:                 channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
14:             // 注册失败,回调通知 promise 异常
15:             } else {
16:                 promise.setFailure(regFuture.cause());
17:             }
18:         }
19:     });
20: }
  • 第 7 行:调用 EventLoop 执行 Channel 的端口绑定逻辑。但是,实际上当前线程已经是 EventLoop 所在的线程了,为何还要这样操作呢?答案在【第 5 至 6 行】的英语注释。感叹句,Netty 虽然代码量非常庞大且复杂,但是英文注释真的是非常齐全,包括 Github 的 issue 对代码提交的描述,也非常健全。

  • 第 14 至 17 行:注册失败,回调通知 promise 异常。

  • 第 11 至 13 行:注册成功,调用

    Channel#bind(SocketAddress localAddress, ChannelPromise promise)
    

    方法,执行 Channel 的端口绑定逻辑。后续的方法栈调用如下图:

    Channel bind 流程

    Channel bind 流程

    • 还是老样子,我们先省略掉 pipeline 的内部实现代码,从 AbstractUnsafe#bind(final SocketAddress localAddress, final ChannelPromise promise) 方法,继续向下分享。

AbstractUnsafe#bind(final SocketAddress localAddress, final ChannelPromise promise) 方法,Channel 的端口绑定逻辑。代码如下:

 1: @Override
 2: public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
 3:     // 判断是否在 EventLoop 的线程中。
 4:     assertEventLoop();
 5: 
 6:     if (!promise.setUncancellable() || !ensureOpen(promise)) {
 7:         return;
 8:     }
 9: 
10:     // See: https://github.com/netty/netty/issues/576
11:     if (Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) &&
12:         localAddress instanceof InetSocketAddress &&
13:         !((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress() &&
14:         !PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
15:         // Warn a user about the fact that a non-root user can't receive a
16:         // broadcast packet on *nix if the socket is bound on non-wildcard address.
17:         logger.warn(
18:                 "A non-root user can't receive a broadcast packet if the socket " +
19:                 "is not bound to a wildcard address; binding to a non-wildcard " +
20:                 "address (" + localAddress + ") anyway as requested.");
21:     }
22: 
23:     // 记录 Channel 是否激活
24:     boolean wasActive = isActive();
25: 
26:     // 绑定 Channel 的端口
27:     try {
28:         doBind(localAddress);
29:     } catch (Throwable t) {
30:         safeSetFailure(promise, t);
31:         closeIfClosed();
32:         return;
33:     }
34: 
35:     // 若 Channel 是新激活的,触发通知 Channel 已激活的事件。
36:     if (!wasActive && isActive()) {
37:         invokeLater(new Runnable() {
38:             @Override
39:             public void run() {
40:                 pipeline.fireChannelActive();
41:             }
42:         });
43:     }
44: 
45:     // 回调通知 promise 执行成功
46:     safeSetSuccess(promise);
47: }
  • 第 4 行:调用 #assertEventLoop() 方法,判断是否在 EventLoop 的线程中。即该方法,只允许在 EventLoop 的线程中执行。代码如下:

    // AbstractUnsafe.java
    private void assertEventLoop() {
        assert !registered || eventLoop.inEventLoop();
    }
    
  • 第 6 至 8 行:和 #register0(...) 方法的【第 5 至 8 行】的代码,是一致的。

  • 第 10 至 21 行:https://github.com/netty/netty/issues/576

  • 第 24 行:调用 #isActive() 方法,获得 Channel 是否激活( active )。NioServerSocketChannel 对该方法的实现代码如下:

    // NioServerSocketChannel.java
    
    @Override
    public boolean isActive() {
        return javaChannel().socket().isBound();
    }
    
    • NioServerSocketChannel 的 #isActive() 的方法实现,判断 ServerSocketChannel 是否绑定端口。此时,一般返回的是 false
  • 第 28 行:调用 #doBind(SocketAddress localAddress) 方法,绑定 Channel 的端口。代码如下:

    // NioServerSocketChannel.java
    
    @Override
    protected void doBind(SocketAddress localAddress) throws Exception {
        if (PlatformDependent.javaVersion() >= 7) {
            javaChannel().bind(localAddress, config.getBacklog());
        } else {
            javaChannel().socket().bind(localAddress, config.getBacklog());
        }
    }
    
    • 【重要】到了此处,服务端的 Java 原生 NIO ServerSocketChannel 终于绑定端口。😊
  • 第 36 行:再次调用 #isActive() 方法,获得 Channel 是否激活。此时,一般返回的是 true 。因此,Channel 可以认为是新激活的,满足【第 36 至 43 行】代码的执行条件。

    • 第 37 行:调用 #invokeLater(Runnable task) 方法,提交任务,让【第 40 行】的代码执行,异步化。代码如下:

      // AbstractUnsafe.java
      private void invokeLater(Runnable task) {
          try {
              // This method is used by outbound operation implementations to trigger an inbound event later.
              // They do not trigger an inbound event immediately because an outbound operation might have been
              // triggered by another inbound event handler method.  If fired immediately, the call stack
              // will look like this for example:
              //
              //   handlerA.inboundBufferUpdated() - (1) an inbound handler method closes a connection.
              //   -> handlerA.ctx.close()
              //      -> channel.unsafe.close()
              //         -> handlerA.channelInactive() - (2) another inbound handler method called while in (1) yet
              //
              // which means the execution of two inbound handler methods of the same handler overlap undesirably.
              eventLoop().execute(task);
          } catch (RejectedExecutionException e) {
              logger.warn("Can't invoke task later as EventLoop rejected it", e);
          }
      }
      
      • 从实现代码可以看出,是通过提交一个新的任务到 EventLoop 的线程中。
      • 英文注释虽然有丢丢长,但是胖友耐心看完。有道在手,英文不愁啊。
    • 第 40 行:调用 DefaultChannelPipeline#fireChannelActive() 方法,触发 Channel 激活的事件。详细解析,见 「3.13.3 beginRead」

  • 第 46 行:调用 #safeSetSuccess(ChannelPromise) 方法,回调通知 promise 执行成功。此处的通知,对应回调的是我们添加到 #bind(...) 方法返回的 ChannelFuture 的 ChannelFutureListener 的监听器。示例代码如下:

    ChannelFuture f = b.bind(PORT).addListener(new ChannelFutureListener() { // 回调的就是我!!!
        @Override
        public void operationComplete(ChannelFuture future) throws Exception {
            System.out.println("测试下被触发");
        }
    }).sync();
    

3.11.7、 beginRead

#bind(final SocketAddress localAddress, final ChannelPromise promise) 方法的【第 40 行】代码,调用 Channel#bind(SocketAddress localAddress, ChannelPromise promise) 方法,触发 Channel 激活的事件。后续的方法栈调用如下图:触发 Channel 激活的事件触发 Channel 激活的事件

* 还是老样子,我们先省略掉 pipeline 的内部实现代码,从 `AbstractUnsafe#beginRead()` 方法,继续向下分享。

AbstractUnsafe#beginRead() 方法,开始读取操作。代码如下:

@Override
public final void beginRead() {
    // 判断是否在 EventLoop 的线程中。
    assertEventLoop();

    // Channel 必须激活
    if (!isActive()) {
        return;
    }

    // 执行开始读取
    try {
        doBeginRead();
    } catch (final Exception e) {
        invokeLater(new Runnable() {
            @Override
            public void run() {
                pipeline.fireExceptionCaught(e);
            }
        });
        close(voidPromise());
    }
}
  • 调用 Channel#doBeginRead() 方法,执行开始读取。对于 NioServerSocketChannel 来说,该方法实现代码如下:

    // AbstractNioMessageChannel.java
    @Override
    protected void doBeginRead() throws Exception {
        if (inputShutdown) {
            return;
        }
        super.doBeginRead();
    }
    
    // AbstractNioChannel.java
    @Override
    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);
        }
    }
    
    • 【重要】在最后几行,我们可以看到,调用 SelectionKey#interestOps(ops) 方法,将我们创建 NioServerSocketChannel 时,设置的 readInterestOp = SelectionKey.OP_ACCEPT 添加为感兴趣的事件。也就说,服务端可以开始处理客户端的连接事件。

4、Bootstract

io.netty.bootstrap.ServerBootstrap下面值是重点指出了重要的方法实现。其它源码与父类io.netty.bootstrap.AbstractBootstrap大同小异。

4.1、构造方法

  // 子Channel的可选集合
    private final Map<ChannelOption<?>, Object> childOptions = new ConcurrentHashMap<ChannelOption<?>, Object>();
    // 子Channel的属性集合
    private final Map<AttributeKey<?>, Object> childAttrs = new ConcurrentHashMap<AttributeKey<?>, Object>();
    // 启动类 配置对象
    private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);
    // 子Channel 的EventLoopGroup对象
    private volatile EventLoopGroup childGroup;
    // 子Channel 的处理器
    private volatile ChannelHandler childHandler;

    public ServerBootstrap() {
    }

    private ServerBootstrap(ServerBootstrap bootstrap) {
        super(bootstrap);
        childGroup = bootstrap.childGroup;
        childHandler = bootstrap.childHandler;
        childOptions.putAll(bootstrap.childOptions);
        childAttrs.putAll(bootstrap.childAttrs);
    }

4.2、init

    @Override
    void init(Channel channel) {
        // 初始化Channel 的可选想集合
        setChannelOptions(channel, options0().entrySet().toArray(EMPTY_OPTION_ARRAY), logger);
        // 初始化 Channel的属性集合
        setAttributes(channel, attrs0().entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY));

        ChannelPipeline p = channel.pipeline();

        final EventLoopGroup currentChildGroup = childGroup;
        final ChannelHandler currentChildHandler = childHandler;
        final Entry<ChannelOption<?>, Object>[] currentChildOptions =
                childOptions.entrySet().toArray(EMPTY_OPTION_ARRAY);
        final Entry<AttributeKey<?>, Object>[] currentChildAttrs = childAttrs.entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY);

        // 添加 ChannelInitializer 对象到 pipeline 中,用于后续初始化 ChannelHandler 到 pipeline 中。
        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(final Channel ch) {
                final ChannelPipeline pipeline = ch.pipeline();
                // 添加配置的 ChannelHandler 到 pipeline 中。
                ChannelHandler handler = config.handler();
                if (handler != null) {
                    pipeline.addLast(handler);
                }
                // 添加 ServerBootstrapAcceptor 到 pipeline 中。
                // 使用 EventLoop 执行的原因,
                // 参见 https://github.com/lightningMan/netty/commit/4638df20628a8987c8709f0f8e5f3679a914ce1a
                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });
    }
  • 创建 ServerBootstrapAcceptor 对象,添加到 pipeline 中。为什么使用 EventLoop 执行添加的过程?如果启动器配置的处理器,并且 ServerBootstrapAcceptor 不使用 EventLoop 添加,则会导致 ServerBootstrapAcceptor 添加到配置的处理器之前。示例代码如下:

    ServerBootstrap b = new ServerBootstrap();
    b.handler(new ChannelInitializer<Channel>() {
    
        @Override
        protected void initChannel(Channel ch) {
            final ChannelPipeline pipeline = ch.pipeline();
            ch.eventLoop().execute(new Runnable() {
                @Override
                public void run() {
                    pipeline.addLast(new LoggingHandler(LogLevel.INFO));
                }
            });
        }
    
    });
    
  • ServerBootstrapAcceptor 也是一个 ChannelHandler 实现类,用于接受客户端的连接请求。详细解析,见后续文章。

  • 该 ChannelInitializer 的初始化的执行,在 AbstractChannel#register0(ChannelPromise promise) 方法中触发执行。

  • 那么为什么要使用 ChannelInitializer 进行处理器的初始化呢?而不直接添加到 pipeline 中。例如修改为如下代码:

    final Channel ch = channel;
    final ChannelPipeline pipeline = ch.pipeline();
    
    // 添加配置的 ChannelHandler 到 pipeline 中。
    ChannelHandler handler = config.handler();
    if (handler != null) {
        pipeline.addLast(handler);
    }
    
    // 添加 ServerBootstrapAcceptor 到 pipeline 中。
    // 使用 EventLoop 执行的原因,参见 https://github.com/lightningMan/netty/commit/4638df20628a8987c8709f0f8e5f3679a914ce1a
    ch.eventLoop().execute(new Runnable() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread() + ": ServerBootstrapAcceptor");
            pipeline.addLast(new ServerBootstrapAcceptor(
                    ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
        }
    });
    
    • 因为此时 Channel 并未注册到 EventLoop 中。如果调用 EventLoop#execute(Runnable runnable) 方法,会抛出 Exception in thread "main" java.lang.IllegalStateException: channel not registered to an event loop 异常。

    更多源码信息参见:码云

引用:

一个例子让就能你彻底理解Java的Future模式,Future类的设计思想

netty源码分析之-Future、ChannelFuture与ChannelPromise详解

开源异步并行框架,完成任意的多线程编排、阻塞、等待、串并行结合、强弱依赖

本文仅供笔者本人学习,有错误的地方还望指出,一起进步!望海涵!

关于Netty核心组件简介:Netty核心组件简介

转载请注明出处!

欢迎关注我的公共号,无广告,不打扰。不定时更新Java后端知识,我们一起超神。
qrcode.jpg

——努力努力再努力xLg

加油!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值