02-Bootstrap和ServerBootstrap

Bootstrap和ServerBootstrap

  • Bootstrap和ServerBootstrap分别是Netty客户端和服务端的启动器,编写Netty应用时也是从这两个类开始的,具体可以参考 :Netty启动分析和组件设计
  • 继承关系如下:

在这里插入图片描述

一、AbstractBootstrap

  • AbstractBootstrap是一个抽象类,Bootstrap和ServerBootstrap的都继承自AbstractBootstrap抽象类。AbstractBootstrap提供了类似于建造者模式的相关方法,我们可以通过方法链来添加配置参数,下面是带注释源码,篇幅原因部分源码已经省略,包含属性和属性设置相关部分;
/**
 * AbstractBootstrap是一个辅助类来帮助我们更好的启动Channel,它提供了一系列的方法链来帮助我们更好的配置AbstractBootstrap
 */
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {

    /**
     * EventLoopGroup对象
     */
    volatile EventLoopGroup group;
    /**
     * Channel工厂,用于创建 Channel 对象。
     */
    @SuppressWarnings("deprecation")
    private volatile ChannelFactory<? extends C> channelFactory;
    /**
     * 本地地址
     */
    private volatile SocketAddress localAddress;
    /**
     * channel相关的选项参数
     */
    private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
    /**
     * 初始化channel的属性值
     */
    private final Map<AttributeKey<?>, Object> attrs = new LinkedHashMap<AttributeKey<?>, Object>();
    /**
     * 业务逻辑Handler,可能是HandlerInitializer,也可能是普通Handler
     */
    private volatile ChannelHandler handler;

    /**
     * 构造方法
     * */
    AbstractBootstrap(AbstractBootstrap<B, C> bootstrap) {
        group = bootstrap.group;
        channelFactory = bootstrap.channelFactory;
        handler = bootstrap.handler;
        localAddress = bootstrap.localAddress;
        synchronized (bootstrap.options) {
            options.putAll(bootstrap.options);
        }
        synchronized (bootstrap.attrs) {
            attrs.putAll(bootstrap.attrs);
        }
    }

    /**
     * 配置EventLoopGroup,EventLoopGroup用于处理I/O事件的事件循环组。是异步事件模型的重要实现。
     */
    public B group(EventLoopGroup group) {
        if (group == null) {
            throw new NullPointerException("group");
        }
        if (this.group != null) { // 不允许重复设置
            throw new IllegalStateException("group set already");
        }
        this.group = group;
        return self();
    }

    /**
     * 设置通道类型,可以使用NioServerSocketChannel或者NioSocketChannel做参数,注释表示使用传入的类来创建Channel实例
     */
    public B channel(Class<? extends C> channelClass) {
        if (channelClass == null) {
            throw new NullPointerException("channelClass");
        }
        return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
    }

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

    /**
     * 设置绑定的ip端口,一般用于服务端
     */
    public B localAddress(SocketAddress localAddress) {
        this.localAddress = localAddress;
        return self();
    }

    /**
     * 设置ChannelOption选项,在Channel被创建的时候会使用这些选项,value传入null可以移除ChannelOption
     */
    public <T> B option(ChannelOption<T> option, T value) {
        if (option == null) {
            throw new NullPointerException("option");
        }
        if (value == null) {
            // 空,意味着移除
            synchronized (options) {
                options.remove(option);
            }
        } else { // 非空,进行修改
            synchronized (options) {
                options.put(option, value);
            }
        }
        return self();
    }

    /**
     * 返回深拷贝,由子类实现
     */
    @Override
    @SuppressWarnings("CloneDoesntDeclareCloneNotSupportedException")
    public abstract B clone();

    /**
     * Create a new {@link Channel} and register it with an {@link EventLoop}.
     * 创建Channel并且将Channel注册到EventLoop
     */
    public ChannelFuture register() {
        validate();
        return initAndRegister();
    }

    /**
     * 创建Channel并绑定
     */
    public ChannelFuture bind() {
        //1.校验服务启动的必要参数
        validate();
        SocketAddress localAddress = this.localAddress;
        if (localAddress == null) {
            throw new IllegalStateException("localAddress not set");
        }
        //2.绑定地址端口
        return doBind(localAddress);
    }

     /**
     * 客户端和服务端实现不同,交给子类重写
     */
    abstract void init(Channel channel) throws Exception;

    /**
     * 设置处理请求的ChannelHandler
     */
    public B handler(ChannelHandler handler) {
        if (handler == null) {
            throw new NullPointerException("handler");
        }
        this.handler = handler;
        return self();
    }

    /**
     * 返回AbstractBootstrapConfig的config对象,可以获取当前bootstrap的config,由子类实现
     */
    public abstract AbstractBootstrapConfig<B, C> config();

    /**
     * 设置ChannelOptions,子类中调用来初始化Channel的可选项集合
     */
    static void setChannelOptions(Channel channel, Map<ChannelOption<?>, Object> options, InternalLogger logger) {
        for (Map.Entry<ChannelOption<?>, Object> e : options.entrySet()) {
            setChannelOption(channel, e.getKey(), e.getValue(), logger);
        }
    }
}
  • 这里面大部分方法都是设置相关的参数,对于channel方法需要注意,channel设置通道类型,内部实际上根据传入的类型参数初始化好了ChannelFactory<? extends C> channelFactory这个Channel工厂对象,后续会使用该工厂对象来生产Channel对象。这里面默认是使用ReflectiveChannelFactory这个ChannelFactory的实现类,内部采用反射的方式创建Channel对象,下面给出一点核心代码
/**
 * 创建Channel的ChannelFactory对象,使用构造方法反射创建,名字也能看出来
 */
public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {

    /**
     * Channel 对应的类
     */
    private final Class<? extends T> clazz;

    /**
     * 构造方法将对应的Channel类型传进来
     */
    public ReflectiveChannelFactory(Class<? extends T> clazz) {
        if (clazz == null) {
            throw new NullPointerException("clazz");
        }
        this.clazz = clazz;
    }

    @Override
    public T newChannel() {
        try {
            //1.反射调用默认构造方法,创建Channel对象
            return clazz.getConstructor().newInstance();
        } catch (Throwable t) {
            throw new ChannelException("Unable to create Channel from class " + clazz, t);
        }
    }
}
  • option方法设置ChannelOption选项,可以对通道的属性和行为进行一定的定制,比较典型的有:通过b.option(ChannelOption.SO_KEEPALIVE, true)来设置连接通道保持连接,这也是一个非常重要的方法。
  • handler方法设置事件处理器来处理用户的逻辑,不过一般会设置一个ChannelInitializer的实现类来设置用户的若干个具体ChannelHandler

1.1 doBind和doBind0

  • doBind是绑定的主体流程
/**
     * 绑定的主流程,各种重载的bind方法底层走到该方法
     * 主要包括:initAndRegister和doBind0,
     * initAndRegister是创建Channel和绑定用户的Handler,以及将Channel注册到Reactor
     * doBind0是绑定端口
     */
    private ChannelFuture doBind(final SocketAddress localAddress) {
        //1.初始化并注册一个 Channel 对象,注册是异步的过程,因此返回一个ChannelFuture对象。
        final ChannelFuture regFuture = initAndRegister();
        final Channel channel = regFuture.channel();
        //2.发生异常,直接返回
        if (regFuture.cause() != null) {
            return regFuture;
        }

        if (regFuture.isDone()) { // 未
            //3.到此说明创建Channel和绑定完成且是成功的,因此绑定Channel的端口,并注册 Channel 到 SelectionKey 中。
            // 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 {
                    System.out.println(Thread.currentThread() + ": PendingRegistrationPromise");
                    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;
        }
    }
  • doBind0是绑定的具体实现
    private static void doBind0( final ChannelFuture regFuture, final Channel channel,final SocketAddress localAddress, final ChannelPromise promise) {

        // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up
        // the pipeline in its channelRegistered() implementation.
        channel.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread() + ": bind");
                // 注册成功,绑定端口
                if (regFuture.isSuccess()) {
                    channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
                    // 注册失败,回调通知 promise 异常
                } else {
                    promise.setFailure(regFuture.cause());
                }
            }
        });
    }

1.2 initAndRegister

  • initAndRegister是初始化和注册Channel的主流程,内部会调用init方法
/**
     * 初始化并注册一个 Channel 对象,注册是异步的过程,返回一个ChannelFuture对象。
     * initAndRegister主要包括:创建Channel和绑定用户的Handler,以及将Channel注册到Reactor
     */
    final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            //1.创建Channel对象,通过ChannelFactory创建
            channel = channelFactory.newChannel();
            //2.初始化Channel配置
            init(channel);
        } catch (Throwable t) {
            //3.异常处理,当实例化或者初始化抛出异常的情况下则关闭通道并且返回一个失败状态的ChannelFuture对象。
            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
                return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
            }
            // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
            return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
        }

        //4.注册 Channel 到 EventLoopGroup 中 ,返回一个ChannelFuture对象,如果注册失败则关闭连接通道。
        ChannelFuture regFuture = config().group().register(channel);
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly(); // 强制关闭 Channel
            }
        }

        // 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.
        //5.返回regFutre对象,表示未来注册的结果。如果成功了则表示已经注册成功,则可以安全地调用bind()或者connect方法进行下一步处理了。
        return regFuture;
    }

二、Bootstrap

  • Bootstrap是客户端的启动器,除了继承自AbstractBootstrap之外,Bootstrap本身主要是完成对远程地址的连接,因此内部的属性方法主要是围绕完成连接做的,另外也重写了父类的抽象方法init来完成Channel的初始化。
  • 注释代码如下:
/**
 * 便于客户端启动Channel
 */
public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {

    /**
     * 默认地址解析器对象
     */
    private static final AddressResolverGroup<?> DEFAULT_RESOLVER = DefaultAddressResolverGroup.INSTANCE;

    /**
     * Bootstrap启动器配置对象
     */
    private final BootstrapConfig config = new BootstrapConfig(this);
    /**
     * 地址解析器对象
     */
    @SuppressWarnings("unchecked")
    private volatile AddressResolverGroup<SocketAddress> resolver = (AddressResolverGroup<SocketAddress>) DEFAULT_RESOLVER;

    /**
     * 远程连接地址
     */
    private volatile SocketAddress remoteAddress;

    /**
     * 构造方法
     */
    private Bootstrap(Bootstrap bootstrap) {
        super(bootstrap);
        resolver = bootstrap.resolver;
        remoteAddress = bootstrap.remoteAddress;
    }

    /**
     * 设置地址解析器
     */
    @SuppressWarnings("unchecked")
    public Bootstrap resolver(AddressResolverGroup<?> resolver) {
        this.resolver = (AddressResolverGroup<SocketAddress>) (resolver == null ? DEFAULT_RESOLVER : resolver);
        return this;
    }

    /**
     * 设置远程地址
     */
    public Bootstrap remoteAddress(SocketAddress remoteAddress) {
        this.remoteAddress = remoteAddress;
        return this;
    }

    /**
     * Connect a {@link Channel} to the remote peer.
     * 连接到远程地址
     */
    public ChannelFuture connect() {
        //1.参数校验
        validate();
        SocketAddress remoteAddress = this.remoteAddress;
        if (remoteAddress == null) {
            throw new IllegalStateException("remoteAddress not set");
        }
        //2.地址解析并连接
        return doResolveAndConnect(remoteAddress, config.localAddress());
    }
}

2.1 connect

  • connect方法是Bootstrap最重要的一个方法,当构造以及配置好了之后,通过连接方法开始建立连接以及后续的发送和接收信息处理。提供了一系列重载的方法,逻辑完全是一样的,我们这里这分析其中一个方法。
    public ChannelFuture connect(SocketAddress remoteAddress) {
        //1.参数校验
        validate();
        if (remoteAddress == null) {
            throw new NullPointerException("remoteAddress");
        }
        //2.地址解析并连接
        return doResolveAndConnect(remoteAddress, config.localAddress());
    }
  • 我们看到connect校验参数之后,是通过doResolveAndConnect来完成连接过程的,validate逻辑比较简单就不解析了

2.2 doResolveAndConnect和doResolveAndConnect0

  • doResolveAndConnect完成地址解析并连接,内部会调用父类的initAndRegister方法,父类的initAndRegister方法里面又会走子类重写的init方法
    /**
     * @see #connect()
     * 解析地址并连接的实现主体
     */
    private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
        
        //1.初始化并注册Channel对象,异步注册返回一个ChannelFuture对象
        final ChannelFuture regFuture = initAndRegister();
        //得到初始化和注册好的Channel对象
        final Channel channel = regFuture.channel();

        if (regFuture.isDone()) {
            //2.若执行失败,直接返回
            if (!regFuture.isSuccess()) {
                return regFuture;
            }
            //3.如果执行成功,就解析地址并连接
            return doResolveAndConnect0(channel, remoteAddress, localAddress, channel.newPromise());
        } else {
            //4.如果未完成,则给ChannelFuture添加一个监听器,当事件到达的时候执行响应处理,
            // Registration future is almost always fulfilled already, but just in case it's not.
            final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
            //5.添加一个监听器用来监听操作完成事件operationComplete。
            regFuture.addListener(new ChannelFutureListener() {

                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    // Directly obtain the cause and do a null check so we only need one volatile read in case of a
                    // failure.
                    Throwable cause = future.cause();
                    if (cause != null) {
                        //6.判断若操作完成的时候有异常,则设置PendingRegistrationPromise为失败。
                        // 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 {
                        //7.若执行成功,则设置PendingRegistrationPromise为注册成功,最后也会调用doResolveAndConnect0方法,这与第3步是一样的。
                        // Registration was successful, doResolveAndConnect0so set the correct executor to use.
                        // See https://github.com/netty/netty/issues/2586
                        promise.registered();

                        // 解析地址并连接
                        doResolveAndConnect0(channel, remoteAddress, localAddress, promise);
                    }
                }

            });
            return promise;
        }
    }
  • doResolveAndConnect0
/**
     * @see #connect()
     * 解析地址并连接的实现
     */
    private ChannelFuture doResolveAndConnect0(final Channel channel, SocketAddress remoteAddress,
                                               final SocketAddress localAddress, final ChannelPromise promise) {
        try {
            //1.得到当前Channel绑定的一个EventLoop对象和与当前EventLoop对象绑定的一个地址解析器对象
            final EventLoop eventLoop = channel.eventLoop();
            final AddressResolver<SocketAddress> resolver = this.resolver.getResolver(eventLoop);

            //2.如果地址解析器不支持或者已经解析过,则直接使用该远程地址进行连接
            if (!resolver.isSupported(remoteAddress) || resolver.isResolved(remoteAddress)) {
                // Resolver has no idea about what to do with the specified remote address or it's resolved already.
                doConnect(remoteAddress, localAddress, promise);
                return promise;
            }

            //3.解析地址
            final Future<SocketAddress> resolveFuture = resolver.resolve(remoteAddress);

            //4.如果解析器正好解析完成,则判断解析结果;如果解析抛出异常则关闭连接,设置状态是失败;如果解析成功则进行连接操作。
            if (resolveFuture.isDone()) {
                // 解析远程地址失败,关闭 Channel ,并回调通知 promise 异常
                final Throwable resolveFailureCause = resolveFuture.cause();
                if (resolveFailureCause != null) {
                    // Failed to resolve immediately
                    channel.close();
                    promise.setFailure(resolveFailureCause);
                } else {
                    // Succeeded to resolve immediately; cached? (or did a blocking lookup)
                    // 连接远程地址
                    doConnect(resolveFuture.getNow(), localAddress, promise);
                }
                return promise;
            }

            // Wait until the name resolution is finished.
            //5.如果一段时间后才解析完成,则添加一个监听器,监听未来监听的事件。处理逻辑与4相同
            resolveFuture.addListener(new FutureListener<SocketAddress>() {
                @Override
                public void operationComplete(Future<SocketAddress> future) throws Exception {
                    // 解析远程地址失败,关闭 Channel ,并回调通知 promise 异常
                    if (future.cause() != null) {
                        channel.close();
                        promise.setFailure(future.cause());
                        // 解析远程地址成功,连接远程地址
                    } else {
                        doConnect(future.getNow(), localAddress, promise);
                    }
                }
            });
        } catch (Throwable cause) {
            // 发生异常,并回调通知 promise 异常
            promise.tryFailure(cause);
        }
        return promise;
    }

2.3 init

  • init方法初始化Channel
    /**
     * 初始化Channel
     * */
    @Override
    void init(Channel channel) throws Exception {
        //1.获取Channel的pipeline,pipeline是在创建Channel的同时创建的
        ChannelPipeline p = channel.pipeline();

        //2.添加处理器到pipeline
        p.addLast(config.handler());

        //3.初始化Channel的选项集合
        final Map<ChannelOption<?>, Object> options = options0();
        synchronized (options) {
            setChannelOptions(channel, options, logger);
        }

        //4.初始化 Channel 的属性集合
        final Map<AttributeKey<?>, Object> attrs = attrs0();
        synchronized (attrs) {
            for (Entry<AttributeKey<?>, Object> e : attrs.entrySet()) {
                channel.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
            }
        }
    }

2.4 doConnect

  • doConnect里面还是异步去执行连接操作
    /**
     * connect连接的主体
     */
    private static void doConnect(final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise connectPromise) {

        // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up
        // the pipeline in its channelRegistered() implementation.
        final Channel channel = connectPromise.channel();
        channel.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                if (localAddress == null) {
                    channel.connect(remoteAddress, connectPromise);
                } else {
                    channel.connect(remoteAddress, localAddress, connectPromise);
                }
                connectPromise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
            }
        });
    }

三、ServerBootstrap

  • ServerBootstrap是服务端的启动器, ServerBootstrap也实现了父类的init方法用于Channel的初始化,关键属性包括childGroup(对应Reactor中的subReactor)、childHandler(childGroup的事件处理器)、其他的比如选项,配置,参数校验之类的和前面2个类差不多,这里就看下关键的地方

3.1 属性和构造方法

public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {

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


    private ServerBootstrap(ServerBootstrap bootstrap) {
        super(bootstrap);
        childGroup = bootstrap.childGroup;
        childHandler = bootstrap.childHandler;
        synchronized (bootstrap.childOptions) {
            childOptions.putAll(bootstrap.childOptions);
        }
        synchronized (bootstrap.childAttrs) {
            childAttrs.putAll(bootstrap.childAttrs);
        }
    }
  • ServerBootstrap有两个EventLoopGroup的属性,一个是继承自AbstractBootstrap的group(parentGroup),另一个是自定义的childGroup,其中parentGroup负责处理ServerChannel(接收客户端连接通道)的I/O事件, 而chidGroup则是用于处理Channel(客户端连接通道)的I/O事件。

3.2 bind方法

  • bind方法用于创建Channel并绑定,bind有很多重载方法,我们分析一个
    /**
     * 创建Channel并绑定
     */
    public ChannelFuture bind() {
        //1.校验服务启动的必要参数
        validate();
        SocketAddress localAddress = this.localAddress;
        if (localAddress == null) {
            throw new IllegalStateException("localAddress not set");
        }
        //2.绑定地址端口
        return doBind(localAddress);
    }
  • doBind是主逻辑;分析发现doBind和Bootstrap的doResolveAndConnect方法很类似
/**
     * 绑定的主流程,各种重载的bind方法底层走到该方法
     * 主要包括:initAndRegister和doBind0,
     * initAndRegister是创建Channel和绑定用户的Handler,以及将Channel注册到Reactor
     * doBind0是绑定端口
     */
    private ChannelFuture doBind(final SocketAddress localAddress) {
        //1.初始化并注册一个 Channel 对象,注册是异步的过程,因此返回一个ChannelFuture对象。
        final ChannelFuture regFuture = initAndRegister();
        final Channel channel = regFuture.channel();
        //2.发生异常,直接返回
        if (regFuture.cause() != null) {
            return regFuture;
        }


        if (regFuture.isDone()) { 
            //3.到此说明创建Channel和绑定完成且是成功的,因此绑定Channel的端口,并注册 Channel 到 SelectionKey 中。
            // 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 {
                    System.out.println(Thread.currentThread() + ": PendingRegistrationPromise");
                    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.3 init方法

  • 重写init方法初始化Channel
    /**
     * 初始化Channel
     * */
    @Override
    void init(Channel channel) throws Exception {
        //1.初始化Channel选项集合
        final Map<ChannelOption<?>, Object> options = options0();
        synchronized (options) {
            setChannelOptions(channel, options, logger);
        }

        //2.初始化Channel属性集合
        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());
            }
        }

        ChannelPipeline p = channel.pipeline();

        //3.记录当前的属性
        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(0));
        }
        synchronized (childAttrs) {
            currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0));
        }

        //4.添加ChannelInitializer对象到pipeline中,用于后续初始化ChannelHandler到pipeline中。
        p.addLast(new ChannelInitializer<Channel>() {

            @Override
            public void initChannel(final Channel ch) throws Exception {
                System.out.println(Thread.currentThread() + ": user handler");
                final ChannelPipeline pipeline = ch.pipeline();

                //5.添加配置的ChannelHandler到pipeline中。
                ChannelHandler handler = config.handler();
                if (handler != null) {
                    pipeline.addLast(handler);
                }

                //6.添加 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));
                    }
                });
            }

        });
    }

3.4 ServerBootstrapAcceptor

  • 内部类ServerBootstrapAcceptor继承ChannelInboundHandlerAdapter,是一个入站处理器,Netty将ServerChannel接受客户端连接的accept事件抽象为Read读取事件。因此重点关注ServerBootstrapAcceptor的channelRead()方法,其完成的工作有:
1.配置Channel,包括Channel上的处理器链,Channel的选项参数及属性键值对。
2.将服务端accept的客户端Channel注册到subReactor线程池的一个线程上
3.4.1 channelRead
  • channelRead代码如下:
public void channelRead(ChannelHandlerContext ctx, Object msg) {
            //1.接受的客户端Channel
            final Channel child = (Channel) msg;
            //2.添加处理器
            child.pipeline().addLast(childHandler);
            //3.设置Channel选项参数
            setChannelOptions(child, childOptions, logger);
            //4.设置Channel属性
            for (Entry<AttributeKey<?>, Object> e: childAttrs) {
                child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
            }

            //5.将客户端channel注册到work EventLoop线程池,注册失败或者抛出异常则关闭channel,异步注册通过监听器来完成对应操作
            try {
                childGroup.register(child).addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        //6.注册失败,关闭客户端的NioSocketChannel
                        if (!future.isSuccess()) {
                            forceClose(child, future.cause());
                        }
                    }

                });
            } catch (Throwable t) {
                //7.发生异常,强制关闭客户端的 NioSocketChannel
                forceClose(child, t);
            }
        }
3.4.2 exceptionCaught
  • exceptionCaught方法用于处理当ServerChannel事件在执行中产生异常时,此时不会关闭ServerChannel,因为还有其他的客户端连接需要处理。Netty处理异常时的机制是:产生异常后暂停接受客户端连接,1s以后再恢复接受连接。
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            final ChannelConfig config = ctx.channel().config();
            if (config.isAutoRead()) {
                //1.关闭接受新的客户端连接
                // stop accept new connections for 1 second to allow the channel to recover
                // See https://github.com/netty/netty/issues/1328
                config.setAutoRead(false);
                //2.发起 1 秒的延迟任务,恢复重启开启接受新的客户端连接
                ctx.channel().eventLoop().schedule(enableAutoReadTask, 1, TimeUnit.SECONDS);
            }

            //2.继续传播 exceptionCaught 给下一个节点
            // still let the exceptionCaught event flow through the pipeline to give the user
            // a chance to do something with it
            ctx.fireExceptionCaught(cause);
        }

四、小结

  • Bootstrap和ServerBootstrap分表是客户端和服务端的启动器,二者都是继承自AbstractBootstrap
  • 在里面的注册绑定逻辑中,大量使用了异步编程模式,这样耗时操作就不会阻塞主线程,然后再通过Future去获取
  • 在继承体系里面使用了模板模式和建造者模式,initAndRegister固定了流程,init交给子类实现,建造者模式可以方便编程时设置参数

五、参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值