• Netty 源码学习——客户端流程分析


    Netty 源码学习——客户端流程分析


    友情提醒: 需要观看者具备一些 NIO 的知识,否则看起来有的地方可能会不明白。

    使用版本依赖

    <dependency>
        <groupId>io.netty</groupId>
        <artifactId>netty-all</artifactId>
        <version>4.1.36.Final</version>
    </dependency>
    

    io.netty.bootstrap.Bootstrap

    一个 Bootstrap 可以轻松来使用 Netty 来构建客户端代码。本文只是说明一下 Netty 客户端的运行流程,并不会对细节模块进行解析说明。

    客户端部分

    public class NioClient {
    	public static void main(String[] args) {
    		EventLoopGroup group = new NioEventLoopGroup();
    		try {
    			Bootstrap bootstrap = new Bootstrap();
    			bootstrap.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {
    				@Override protected void initChannel(SocketChannel ch) throws Exception {
    					ChannelPipeline pipeline = ch.pipeline();
    					pipeline.addLast("stringDecoder", new StringDecoder());
    					pipeline.addLast("stringEncoder", new StringEncoder());
    					pipeline.addLast("clientHandle", new SimpleChannelInboundHandler<String>() {
    						@Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
    						}
    					});
    				}
    			});
    			ChannelFuture future = bootstrap.connect("127.0.0.1", 9898).sync();
    			future.channel().closeFuture().sync();
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			group.shutdownGracefully();
    		}
    	}
    }
    

    以上代码主要做了以下几个事情:

    1. 声明了一个 EventLoopGroup 实例.
    2. 声明一个 Bootstrap 实例.
    3. 给定了一个 Channel 的类型.
    4. 设置数据的处理 Handle.

    EventLoopGroup 的实例化过程

    我们来进去看一看下面 new NioEventLoopGroup() 到底发生了什么。

    EventLoopGroup group = new NioEventLoopGroup();
    

    首先我们需要看下 NioEventLoopGroup 的继承结构。
    在这里插入图片描述

    进入 new NioEventLoopGroup(); 构造器后发现会有好几个重载的构造器的调用,但是我们一步步点进去发现最后我们跟到了 MultithreadEventExecutorGroup 中.

    protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                            EventExecutorChooserFactory chooserFactory, Object... args) {
        if (nThreads <= 0) {
            throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
        }
    
        if (executor == null) {
            executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
        }
    
        children = new EventExecutor[nThreads];
    
        for (int i = 0; i < nThreads; i ++) {
            try {
                children[i] = newChild(executor, args);
            } catch (Exception e) {
                // 省略不必要的代码只说明初始化操作
            } finally {
                // 省略不必要的代码只说明初始化操作
            }
        }
    
        chooser = chooserFactory.newChooser(children);
        // 省略不必要的代码只说明初始化操作
    }
    

    看了上面的代码主要做了一下几件事情.

    1. 校验 nThreads 不指定的话默认值为 CPU 核心数 * 2
    2. 为成员属性 children 实例化指定一个 nThreads 长度的 EventExecutor 类型数组
    3. 初始化 children 数组中每个元素
    4. 为成员属性 chooser 初始化

    初始化 children 数组中每个元素

    @Override
        protected EventLoop newChild(Executor executor, Object... args) throws Exception {
            return new NioEventLoop(this, executor, (SelectorProvider) args[0],
                ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
        }
    

    我们发现返回的是一个 NioEventLoop 的实例,我们继续看看这个构造器里做了些什么?

    NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
                     SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
            super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
            // 省略不必要的代码
            provider = selectorProvider;
            final SelectorTuple selectorTuple = openSelector();
            selector = selectorTuple.selector;
            unwrappedSelector = selectorTuple.unwrappedSelector;
            selectStrategy = strategy;
        }
    
    1. 引入眼帘的首先是调用父类的构造器
    2. provider 赋值顾名思义这个属性应该就是创建 Selector 的功能。
    3. 下面就是声明一个 SelectorTuple 的局部变量从 openSelector(); 方法中返回,为 selector 赋值到这一步 Selector 已经是获取到了但是被 Netty 进行了封装成了 WindowsSelectorImpl 类型
    4. 为 unwrappedSelector 属性赋值
    5. 为 selectStrategy 属性赋值。
      注意: 这个时候 NioEventLoop 中已经持有 selector 的实例了。

    我们继续看 EventLoop 继承结构找到 SingleThreadEventExecutor 的类

    protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
                                            boolean addTaskWakesUp, int maxPendingTasks,
                                            RejectedExecutionHandler rejectedHandler) {
            super(parent);
            this.addTaskWakesUp = addTaskWakesUp;
            this.maxPendingTasks = Math.max(16, maxPendingTasks);
            this.executor = ThreadExecutorMap.apply(executor, this);
            taskQueue = newTaskQueue(this.maxPendingTasks);
            rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
        }
    

    其实看过线程池的会发现有些眼熟,因为最上层的父类实现了 ExecutorService 接口

    1. addTaskWakesUp: 为 true 时
    2. maxPendingTasks: 新任务被拒绝之前的最大待处理任务数
    3. executor: 执行线程的实例有一个 execute 方法
    4. taskQueue: 任务队列
    5. rejectedExecutionHandler: 拒绝策略

    那么一个 NioEventLoop 实例就已经创建好了。那么 MultithreadEventExecutorGroup 类的 children 属性也就初始化好了。下面我们就要初始化 chooser 这个属性.其实很简单,如果线程池的数量是 2 的指数则创建 PowerOfTwoEventExecutorChooser 否则创建 GenericEventExecutorChooser 他们的作用就是在 children 选择出一个来处理请求。

    @Override
        public EventExecutorChooser newChooser(EventExecutor[] executors) {
            if (isPowerOfTwo(executors.length)) {
                return new PowerOfTwoEventExecutorChooser(executors);
            } else {
                return new GenericEventExecutorChooser(executors);
            }
        }
    

    为什么如果是 2 的幂创建 PowerOfTwoEventExecutorChooser 呢,我们来看里面 next 方法,这样的话用 & 运算来代替 % 运算效率会高。真是不放过一丝一毫提升效率的机会啊。

    private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {
            private final AtomicInteger idx = new AtomicInteger();
            private final EventExecutor[] executors;
    
            PowerOfTwoEventExecutorChooser(EventExecutor[] executors) {
                this.executors = executors;
            }
    
            @Override
            public EventExecutor next() {
                return executors[idx.getAndIncrement() & executors.length - 1];
            }
        }
    

    chooser 创建好后我们客户端的 EventLoopGroup 也就创建完毕了。

    NioSocketChannel 的初始化过程

    我们先来看一看 NioSocketChannel 的继承结构图
    在这里插入图片描述

    Bootstrap bootstrap = new Bootstrap();
    			bootstrap.group(group).channel(NioSocketChannel.class);
    

    我们发现后面就是调用 channel 方法来传入一个 NioSocketChannel.class 那一定必须要一探究竟了。

    public B channel(Class<? extends C> channelClass) {
        if (channelClass == null) {
            throw new NullPointerException("channelClass");
        }
        return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
    }
    

    首先实例化了 ReflectiveChannelFactory 对象出来。

    public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
        private final Constructor<? extends T> constructor;
    
        public ReflectiveChannelFactory(Class<? extends T> clazz) {
            ObjectUtil.checkNotNull(clazz, "clazz");
            try {
                this.constructor = clazz.getConstructor();
            } catch (NoSuchMethodException e) {
               
            }
        }
    
        @Override
        public T newChannel() {
            try {
                return constructor.newInstance();
            } catch (Throwable t) {
                throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);
            }
        }
    }
    

    我们通过代码发现通过 ReflectiveChannelFactory 构造器将 NioSocketChannel.class 的对应构造器对象赋值给了 ReflectiveChannelFactory 中的 constructor.再往下看 newChannel() 方法不就是用构造器反射的方式创建出 NioSocketChannel 实例嘛.

    紧接着创建好 ReflectiveChannelFactory 实例后当作参数传入 channelFactory(ChannelFactory channelFactory); 方法中,其实就是将 ReflectiveChannelFactory 的实例赋值给 AbstractBootstrap 的 channelFactory 属性而已

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

    NioSocketChannel 实例化的时机
    这个时候 bootstrap 差不多也设置完了,我们也知道了 channel 的类型是什么其实就是 NioSocketChannel.那也只是一个 ReflectiveChannelFactory 的实例而已,那么什么时候会利用这个 ReflectiveChannelFactory 来实例化 channel 呢,其实就是在 bootstrap.connect 中.

    hannelFuture future = bootstrap.connect("127.0.0.1", 9898).sync();
    

    我们来跟进去发现一个很关键的方法 initAndRegister();

    private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
        final ChannelFuture regFuture = initAndRegister();
    }
    

    在 initAndRegister(); 中发现了实例化的方法

    final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            // 发现在这里执行了实例化的操作
            channel = channelFactory.newChannel();
            init(channel);
        } catch (Throwable t) {
        }
        return regFuture;
    }
    

    执行了 channelFactory.newChannel(); 后自然会调用 NioSocketChannel 的无参构造器。

    private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
    
    public NioSocketChannel() {
        this(DEFAULT_SELECTOR_PROVIDER);
    }
    

    我们发现调用内部构造器的时候传入了 SelectorProvider 实例,我们继续看发现了一个 newSocket 的方法并且将 SelectorProvider 实例传入了进去。

    public NioSocketChannel(SelectorProvider provider) {
        this(newSocket(provider));
    }
    
    private static SocketChannel newSocket(SelectorProvider provider) {
            try {
                // 终于发现了创建了一个 SocketChannel
                return provider.openSocketChannel();
            } catch (IOException e) {
                throw new ChannelException("Failed to open a socket.", e);
            }
        }
    

    但是内部继续讲 SocketChannel 给传入了下一个构造器

    public NioSocketChannel(SocketChannel socket) {
        this(null, socket);
    }
    
    public NioSocketChannel(Channel parent, SocketChannel socket) {
        super(parent, socket);
        config = new NioSocketChannelConfig(this, socket.socket());
    }
    

    好我们发现调用了 super 父类的构造器传入了 parent 是一个 null 和刚才创建的 SocketChannel.

    protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
        super(parent, ch, SelectionKey.OP_READ);
    }
    

    发现它也调用了自己父类的构造方法

    protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        // 继续调用父类构造器
        super(parent);
        // 将 SocketChannel 赋值给 ch
        this.ch = ch;
        // SelectionKey.OP_READ 的值
        this.readInterestOp = readInterestOp;
        try {
            // 将 SocketChannel 设置为非阻塞的
            ch.configureBlocking(false);
        } catch (IOException e) {
        }
    }
    
    protected AbstractChannel(Channel parent) {
        this.parent = parent;
        // id 赋值
        id = newId();
        // Unsafe 赋值实例为 NioSocketChannelUnsafe
        unsafe = newUnsafe();
        // 设置一个管道实例为 DefaultChannelPipeline
        pipeline = newChannelPipeline();
    }
    

    Unsafe 是啥呢?我们接着来看从注释中可以大致看出,会对应到相关的 Java 底层的 Socket 的操作.

    /**
     * <em>Unsafe</em> operations that should <em>never</em> be called from user-code. These methods
     * are only provided to implement the actual transport, and must be invoked from an I/O thread except for the
     * following methods:
     * <ul>
     *   <li>{@link #localAddress()}</li>
     *   <li>{@link #remoteAddress()}</li>
     *   <li>{@link #closeForcibly()}</li>
     *   <li>{@link #register(EventLoop, ChannelPromise)}</li>
     *   <li>{@link #deregister(ChannelPromise)}</li>
     *   <li>{@link #voidPromise()}</li>
     * </ul>
     */
    interface Unsafe {
        RecvByteBufAllocator.Handle recvBufAllocHandle();
        SocketAddress localAddress();
        SocketAddress remoteAddress();
        void register(EventLoop eventLoop, ChannelPromise promise);
        void bind(SocketAddress localAddress, ChannelPromise promise);
        void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise);
        void disconnect(ChannelPromise promise);
        void close(ChannelPromise promise);
        void closeForcibly();
        void deregister(ChannelPromise promise);
        void beginRead();
        void write(Object msg, ChannelPromise promise);
        void flush();
        ChannelPromise voidPromise();
        ChannelOutboundBuffer outboundBuffer();
    }
    

    至此一个完整的 NioSocketChannel 就初始化完成了.

    Channel 的注册过程

    上一步我们了解了 Channel 的初始化过程,下面这个 channel 什么时候注册到 Selector 中呢?
    我们继续看跟进 bootstrap.connect() 代码。
    发现最后跟进的到了 io.netty.bootstrap.Bootstrap#doResolveAndConnect() 方法中调用了内部的 initAndRegister(); 方法.

    private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
        final ChannelFuture regFuture = initAndRegister();
            return promise;
        }
    }
    

    从这个方法名字就可以看出初始化并注册。我们接着往下看

    final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            channel = channelFactory.newChannel();
            init(channel);
        } catch (Throwable t) {
        }
    
        ChannelFuture regFuture = config().group().register(channel);
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }
        return regFuture;
    }
    

    之前我们已经看完了 channelFactory.newChannel(); 中到底发生了什么事情,现在我们可以继续升级往下看 ChannelFuture regFuture = config().group().register(channel); 这一步到底发生了什么。

    首先回去了对应的 group 这个 group 我们显然最开始已经初始化好了是一个 NioEventLoopGroup 并调用了 register() 方法.

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

    先调用了 next() 函数,这个就是根据 PowerOfTwoEventExecutorChooser 还是 GenericEventExecutorChooser 来选择一个 group 执行事情。既然找到了合适的 group 那么接下来就该执行 register 了.

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

    在执行之前先实例化了 DefaultChannelPromise 将 channel 和 this 传递进去保存在 DefaultChannelPromise 实例中,此时 this 就是 group 的实例。

    public DefaultChannelPromise(Channel channel, EventExecutor executor) {
        super(executor);
        this.channel = checkNotNull(channel, "channel");
    }
    
    @Override
    public ChannelFuture register(final ChannelPromise promise) {
        ObjectUtil.checkNotNull(promise, "promise");
        promise.channel().unsafe().register(this, promise);
        return promise;
    }
    

    在 register 中我们发现了,通过 promise.channel() 中的 unsafe 引用调用了 register 方法,此时的 unsafe 实例类型是 NioSocketChannelUnsafe.感觉已经快要看到我们想要看到的东西了,继续前进.

    @Override
    public final void register(EventLoop eventLoop, final ChannelPromise promise) {
        AbstractChannel.this.eventLoop = eventLoop;
    
        if (eventLoop.inEventLoop()) {
            register0(promise);
        } else {
            try {
                eventLoop.execute(new Runnable() {
                    @Override
                    public void run() {
                        register0(promise);
                    }
                });
            } catch (Throwable t) {
            }
        }
    }
    

    结果在这个方法里我们发现了使用 eventLoop 开启了一个任务去执行并且将 ChannelPromise 传了进去。突然发现怎么还没到我们想看到的地方,看源码就是这样绕来绕去有时候自己都不知道绕到了哪里,但是也要硬着头皮去看,不看永远就不知道了,我们继续往下看.

    private void register0(ChannelPromise promise) {
        try {
            if (!promise.setUncancellable() || !ensureOpen(promise)) {
                return;
            }
            boolean firstRegistration = neverRegistered;
            doRegister();
            neverRegistered = false;
            registered = true;
            pipeline.invokeHandlerAddedIfNeeded();
    
            safeSetSuccess(promise);
            pipeline.fireChannelRegistered();
    
            if (isActive()) {
                if (firstRegistration) {
                    pipeline.fireChannelActive();
                } else if (config().isAutoRead()) {
    
                    beginRead();
                }
            }
        } catch (Throwable t) {
        }
    }
    

    上面代码我们发现在里面竟然还调用了 doRegister(); 不过通过名称感觉 do 开头的函数往往就是真正干事情的,其他的函数名感觉就是烟雾弹一层套一层,点进去我们发现是一个空方法等待子类去重写,这个不就是模板方法模式吗,顺便还学习了一下设计模式,那我们去子类实现里看看.

    @Override
    protected void doRegister() throws Exception {
        boolean selected = false;
        for (;;) {
            try {
                selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
                return;
            } catch (CancelledKeyException e) {
                if (!selected) {
                    eventLoop().selectNow();
                    selected = true;
                } else {
                    throw e;
                }
            }
        }
    }
    

    终于我们看到了想要看的东西 javaChannel().register(eventLoop().unwrappedSelector(), 0, this); 使用了之前我们初始化 NioSocketChannel 时创建的 java.nio.channels.SocketChannel 实例执行了 register 传入了之前获取到的 selector 实例注册了进去.

    总的来说, Channel 注册过程所做的工作就是将 Channel 与对应的 EventLoop 关联, 因此这也体现了, 在 Netty 中, 每个 Channel 都会关联一个特定的 EventLoop, 并且这个 Channel 中的所有 IO 操作都是在这个 EventLoop 中执行的; 当关联好 Channel 和 EventLoop 后, 会继续调用底层的 Java NIO SocketChannel 的 register 方法, 将底层的 java.nio.channels.SocketChannel 注册到指定的 selector 中. 通过这两步, 就完成了 Netty Channel 的注册过程.

    客户端连接

    经过前面的该初始化的初始化,该赋值的也都赋值完毕了,终于到了我们的最后一步连接到对应的服务器了.
    我们就直奔主题, 分析一下客户端是如何发起 TCP 连接的.首先还是继续回到我们的 bootstrap.connect() 方法中.继续跟进到 doResolveAndConnect(); 方法.

    private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
        // 初始化并注册 channel
        final ChannelFuture regFuture = initAndRegister();
        // 获取到 channel
        final Channel channel = regFuture.channel();
    
        // 如果异步操作完成
        if (regFuture.isDone()) {
            if (!regFuture.isSuccess()) {
                return regFuture;
            }
            // 执行注册方法
            return doResolveAndConnect0(channel, remoteAddress, localAddress, channel.newPromise());
        } else {
            final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
            // 添加监听事件
            regFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    Throwable cause = future.cause();
                    if (cause != null) {
    
                        promise.setFailure(cause);
                    } else {
    
                        promise.registered();
                        // 执行注册方法
                        doResolveAndConnect0(channel, remoteAddress, localAddress, promise);
                    }
                }
            });
            return promise;
        }
    }
    

    上面代码发现最终都会调用 doResolveAndConnect0() 我们跟进去

    private ChannelFuture doResolveAndConnect0(final Channel channel, SocketAddress remoteAddress,
                                                   final SocketAddress localAddress, final ChannelPromise promise) {
            try {
                // 先获取到 eventLoop
                final EventLoop eventLoop = channel.eventLoop();
                // 封装一个 AddressResolver 对象
                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) {
                    } 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;
        }
    

    我们发现调用到 doConnect() 方法

    private static void doConnect(
                final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise connectPromise) {
        // 获取对应的 NioSocketChannel 实例
        final Channel channel = connectPromise.channel();
        // 使用对应的 Loop 开启一个线程任务在线程中执行连接
        channel.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                if (localAddress == null) {
                    // 使用 NioSocketChannel.connect()
                    channel.connect(remoteAddress, connectPromise);
                } else {
                    channel.connect(remoteAddress, localAddress, connectPromise);
                }
                connectPromise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
            }
        });
    }
    

    后面使用了 channel 中的 pipeline 属性的 connect(); 并且在 pipeline 中使用的 tail 属性的 connect();

     @Override
    public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
        return pipeline.connect(remoteAddress, promise);
    }
    
    @Override
    public final ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
        return tail.connect(remoteAddress, promise);
    }
    

    我们继续跟进

    @Override
    public ChannelFuture connect(
            final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
        // 查找到对应的 handler
        final AbstractChannelHandlerContext next = findContextOutbound(MASK_CONNECT);
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeConnect(remoteAddress, localAddress, promise);
        } else {
            safeExecute(executor, new Runnable() {
                @Override
                public void run() {
                    next.invokeConnect(remoteAddress, localAddress, promise);
                }
            }, promise, null);
        }
        return promise;
    }
    

    上面的代码中有一个关键的地方, 即 final AbstractChannelHandlerContext next = findContextOutbound(), 这里调用 findContextOutbound 方法, 从 DefaultChannelPipeline 内的双向链表的 tail 开始, 不断向前寻找第一个 outbound 为 true 的 AbstractChannelHandlerContext, 然后调用它的 invokeConnect 方法.现在我们不要纠结于这里后面的篇章我们会详细解释 Netty 的 ChannelPipeline 的实现机制.现在只需要了解到这个方法是做什么的.

    之后根据查找出的 AbstractChannelHandlerContext 实例执行对应的 invokeConnect(); 方法参数是需要连接到的 ip, null, promise 实例中包含了 channel.
    [外链图片转存失败(img-rEnlazj3-1565062020632)(:storage54484bdb-a67b-434e-b759-cf0a9848924568bbeb1.png)]

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

    上面代码进行了一次强转之后调用了 connect(); 继续点进去看看

    @Override
    public void connect(
            ChannelHandlerContext ctx,
            SocketAddress remoteAddress, SocketAddress localAddress,
            ChannelPromise promise) {
        unsafe.connect(remoteAddress, localAddress, promise);
    }
    

    我们看到了 unsafe 对象仿佛胜利就在眼前了,接着加把劲看下去.

    @Override
    public final void connect(
            final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
        if (!promise.setUncancellable() || !ensureOpen(promise)) {
            return;
        }
    
        try {
            if (connectPromise != null) {
                // Already a connect in process.
                throw new ConnectionPendingException();
            }
    
            boolean wasActive = isActive();
            // 执行连接操作
            if (doConnect(remoteAddress, localAddress)) {
                fulfillConnectPromise(promise, wasActive);
            } else {
                // 省略一些代码
            }
        } catch (Throwable t) {
            promise.tryFailure(annotateConnectException(t, remoteAddress));
            closeIfClosed();
        }
    }
    

    在里面还调用了doConnect();

    @Override
    protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
        boolean success = false;
        try {
            // 使用工具类连接,传入 channel 和目标 ip
            boolean connected = SocketUtils.connect(javaChannel(), remoteAddress);
            if (!connected) {
                // 增加一个 CONNECT 事件
                selectionKey().interestOps(SelectionKey.OP_CONNECT);
            }
            success = true;
            return connected;
        } finally {
            if (!success) {
                doClose();
            }
        }
    }
    

    我们来跟进 SocketUtils.connect();

    public static boolean connect(final SocketChannel socketChannel, final SocketAddress remoteAddress)
                throws IOException {
        try {
            // 不用做权限检查
            return AccessController.doPrivileged(new PrivilegedExceptionAction<Boolean>() {
                @Override
                public Boolean run() throws IOException {
                    // 最终使用了 socketChannel.connect() 来连接到目标
                    return socketChannel.connect(remoteAddress);
                }
            });
        } catch (PrivilegedActionException e) {
            throw (IOException) e.getCause();
        }
    }
    

    最后我们终于看到的最关键的部分了, 庆祝一下! 最后还是调用了 java.nio.channels.SocketChannel 完成了连接.

    如果写的不足之处望指出谢谢各位观看!

  • 相关阅读:
    Qt实战6.万能的无边框窗口(FramelessWindow)
    Qt实战5.如何获取USB设备信息?
    Qt实战4.简单封装的文件监控
    Qt实战3.Qt仿Win10风格界面
    Linux下使用脚本让程序顺序启动
    项目经验1.软件的开发过程
    Qt实战2.老生常谈的文件传输
    mysql导入txt文件
    linux离线安装python3
    mysql知识点
  • 原文地址:https://www.cnblogs.com/liufeichn/p/11961614.html
Copyright © 2020-2023  润新知