Netty源码解析之Bootstrap

阅读须知

  • Netty版本:4.1.14.Final
  • 文章中使用/**/注释的方法会做深入分析

正文

private void connect(int port, String host) throws Exception {
    EventLoopGroup group = new NioEventLoopGroup();
    try {
        Bootstrap b = new Bootstrap();
        b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true).handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel socketChannel) throws Exception {
                socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024));
                socketChannel.pipeline().addLast(new StringDecoder());
                // ...
            }
        });
        ChannelFuture f = b.connect(host, port).sync();
        f.channel().closeFuture().sync();
    } finally {
        group.shutdownGracefully();
    }
}

这是一个简单的Netty客户端启动的方法,我们以这个方法作为入口来分析Netty的源码实现,首先我们来看Bootstrap的源码实现:

Bootstrap的作用是可以轻松引导用于客户端的Channel,b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true).handler(...);这段代码调用的方法都是在为Bootstrap的各个属性赋值,采用链式编程的方式,下面我们来看connect方法的实现:
Bootstrap:

public ChannelFuture connect(String inetHost, int inetPort) {
    return connect(InetSocketAddress.createUnresolved(inetHost, inetPort));
}

Bootstrap:

public ChannelFuture connect(SocketAddress remoteAddress) {
    if (remoteAddress == null) {
        throw new NullPointerException("remoteAddress");
    }
    validate(); // 校验group、channelFactory等属性不能为null
    /* 建立连接 */
    return doResolveAndConnect(remoteAddress, config.localAddress());
}

Bootstrap:

private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
    /* 注册和初始化Channel */
    final ChannelFuture regFuture = initAndRegister();
    final Channel channel = regFuture.channel();
    // 判断任务是否完成,正常结束、抛异常、取消都算任务完成
    if (regFuture.isDone()) {
        // 如果未成功直接返回
        if (!regFuture.isSuccess()) {
            return regFuture;
        }
        /* 解析并建立连接 */
        return doResolveAndConnect0(channel, remoteAddress, localAddress, channel.newPromise());
    } else {
        // 注册future几乎总是已经完成,但以防万一
        final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
        regFuture.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                // 获取异常并进行非空判断,以便在失败时我们只需要一次volatile读取
                Throwable cause = future.cause();
                if (cause != null) {
                    // EventLoop上的注册失败会导致ChannelPromise直接失败,导致我们尝试访问Channel的EventLoop时不会导致IllegalStateException。
                    promise.setFailure(cause);
                } else {
                    // 注册成功
                    promise.registered();
                    /* 解析并建立连接 */
                    doResolveAndConnect0(channel, remoteAddress, localAddress, promise);
                }
            }
        });
        return promise;
    }
}

AbstractBootstrap:

final ChannelFuture initAndRegister() {
    Channel channel = null;
    try {
        // 这里的channelFactory为调用channel方法时初始化的ReflectiveChannelFactory
        // 也就是根据传入的Class类型反射初始化Channel,示例中我们传入的是NioSocketChannel
        channel = channelFactory.newChannel();
        /* 初始化Channel */
        init(channel);
    } catch (Throwable t) {
        // 如果newChannel崩溃,例如SocketException("too many open files"),channel可以为null
        if (channel != null) {
            // 立即关闭Channel,不会触发任何事件
            channel.unsafe().closeForcibly();
        }
        // 由于Channel尚未注册,我们需要强制使用GlobalEventExecutor
        return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
    }
    // 注册Channel
    ChannelFuture regFuture = config().group().register(channel);
    if (regFuture.cause() != null) {
        if (channel.isRegistered()) {
            // 如果Channel已经使用EventLoop注册,请求在完成操作后关闭Channel的同时通知ChannelFuture
            channel.close();
        } else {
            // 如果Channel未注册,立即关闭Channel,不会触发任何事件
            channel.unsafe().closeForcibly();
        }
    }
    return regFuture;
}

方法中会涉及到两个新的类EventLoopGroup和Promise,我们会用单独的文章进行分析。方法执行到最后并且promise没有失败,则为以下情况之一:
1. 如果我们尝试从event loop注册,则此时注册已完成。即现在尝试bind()或connect()是安全的,因为channel已被注册。
2. 如果我们尝试从另一个线程注册,则注册请求已成功添加到event loop的任务队列中以供稍后执行。
即尝试bind()或connect()现在是安全的:
       因为在注册任务执行后,bind()或connect()将被执行
       因为register(),bind()和connect()都绑定到同一个线程。

Bootstrap:

void init(Channel channel) throws Exception {
    ChannelPipeline p = channel.pipeline();
    // 这里为Channel popeline添加ChannelHandler的是用户指定的,示例中传入的是ChannelInitializer对象
    p.addLast(config.handler());
    final Map<ChannelOption<?>, Object> options = options0();
    synchronized (options) {
        // 将用户配置的option选项设置到Channel的ChannelConfig对象中
        setChannelOptions(channel, options, logger);
    }
    final Map<AttributeKey<?>, Object> attrs = attrs0();
    synchronized (attrs) {
        for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
            // 将用户配置的attr属性设置到Channel的Attribute属性中
            channel.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
        }
    }
}

完成初始化Channel后,下面就是解析并建立连接:
Bootstrap:

private ChannelFuture doResolveAndConnect0(final Channel channel, SocketAddress remoteAddress,
 final SocketAddress localAddress, final ChannelPromise promise) {
    try {
        final EventLoop eventLoop = channel.eventLoop();
        /* 获取与此EventLoop相关联的地址解析器 */
        final AddressResolver<SocketAddress> resolver = this.resolver.getResolver(eventLoop);
        if (!resolver.isSupported(remoteAddress) || resolver.isResolved(remoteAddress)) {
            /* 解析器不支持指定的远程地址的解析,或者已经解析了,直接建立连接 */
            doConnect(remoteAddress, localAddress, promise);
            return promise;
        }
        // 解析器解析远程地址
        final Future<SocketAddress> resolveFuture = resolver.resolve(remoteAddress);
        if (resolveFuture.isDone()) {
            final Throwable resolveFailureCause = resolveFuture.cause();
            // 判断任务完成后之后发生异常
            if (resolveFailureCause != null) {
                channel.close(); // 异常关闭channel
                promise.setFailure(resolveFailureCause);
            } else {
                /* 没有异常则说明解析成功,直接建立连接 */
                doConnect(resolveFuture.getNow(), localAddress, promise);
            }
            return promise;
        }
        // 添加监听器等待解析完成继续做与上面同样的处理
        resolveFuture.addListener(new FutureListener<SocketAddress>() {
            @Override
            public void operationComplete(Future<SocketAddress> future) throws Exception {
                if (future.cause() != null) {
                    channel.close();
                    promise.setFailure(future.cause());
                } else {
                    doConnect(future.getNow(), localAddress, promise);
                }
            }
        });
    } catch (Throwable cause) {
        promise.tryFailure(cause);
    }
    return promise;
}

AddressResolverGroup:

public AddressResolver<T> getResolver(final EventExecutor executor) {
    if (executor == null) {
        throw new NullPointerException("executor");
    }
    if (executor.isShuttingDown()) {
        throw new IllegalStateException("executor not accepting a task");
    }
    AddressResolver<T> r;
    synchronized (resolvers) {
        // 尝试从缓存中获取解析器,解析的初始化操作比较昂贵,放入缓存复用
        r = resolvers.get(executor);
        if (r == null) {
            final AddressResolver<T> newResolver;
            try {
                // 缓存中不存在则新建解析器,最终会返回InetSocketAddressResolver示例,用于解析InetSocketAddress
                newResolver = newResolver(executor);
            } catch (Exception e) {
                throw new IllegalStateException("failed to create a new resolver", e);
            }
            resolvers.put(executor, newResolver); // 放入缓存
            // 添加EventExecutor终止监听器
            executor.terminationFuture().addListener(new FutureListener<Object>() {
                @Override
                public void operationComplete(Future<Object> future) throws Exception {
                    synchronized (resolvers) {
                        // EventExecutor终止时移除缓存
                        resolvers.remove(executor);
                    }
                    newResolver.close(); // 关闭解析器
                }
            });
            r = newResolver;
        }
    }
    return r;
}

远程地址解析操作就是通过InetSocketAddress的getHostName()方法获取主机名解析为InetAddress,使用的是JDK原生的方法进行解析,不多赘述,下面我们来看连接操作:
Bootstrap:

private static void doConnect(
    final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise connectPromise) {
    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);
            }
            // 添加监听器,在失败时关闭Channel
            connectPromise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
        }
    });
}

Channel的连接我们会用单独的文章分析,到这里,Bootstrap引导启动的源码分析就完成了。

猜你喜欢

转载自blog.csdn.net/heroqiang/article/details/80470689