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


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

使用版本依赖

  1. <dependency>
  2. <groupId>io.netty</groupId>
  3. <artifactId>netty-all</artifactId>
  4. <version>4.1.36.Final</version>
  5. </dependency>

io.netty.bootstrap.Bootstrap

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

客户端部分

  1. public class NioClient {
  2. public static void main(String[] args) {
  3. EventLoopGroup group = new NioEventLoopGroup();
  4. try {
  5. Bootstrap bootstrap = new Bootstrap();
  6. bootstrap.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {
  7. @Override protected void initChannel(SocketChannel ch) throws Exception {
  8. ChannelPipeline pipeline = ch.pipeline();
  9. pipeline.addLast("stringDecoder", new StringDecoder());
  10. pipeline.addLast("stringEncoder", new StringEncoder());
  11. pipeline.addLast("clientHandle", new SimpleChannelInboundHandler<String>() {
  12. @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
  13. }
  14. });
  15. }
  16. });
  17. ChannelFuture future = bootstrap.connect("127.0.0.1", 9898).sync();
  18. future.channel().closeFuture().sync();
  19. } catch (Exception e) {
  20. e.printStackTrace();
  21. } finally {
  22. group.shutdownGracefully();
  23. }
  24. }
  25. }

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

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

EventLoopGroup 的实例化过程

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

  1. EventLoopGroup group = new NioEventLoopGroup();

首先我们需要看下 NioEventLoopGroup 的继承结构。

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

  1. protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
  2. EventExecutorChooserFactory chooserFactory, Object... args) {
  3. if (nThreads <= 0) {
  4. throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
  5. }
  6. if (executor == null) {
  7. executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
  8. }
  9. children = new EventExecutor[nThreads];
  10. for (int i = 0; i < nThreads; i ++) {
  11. try {
  12. children[i] = newChild(executor, args);
  13. } catch (Exception e) {
  14. // 省略不必要的代码只说明初始化操作
  15. } finally {
  16. // 省略不必要的代码只说明初始化操作
  17. }
  18. }
  19. chooser = chooserFactory.newChooser(children);
  20. // 省略不必要的代码只说明初始化操作
  21. }

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

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

初始化 children 数组中每个元素

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

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

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

    注意: 这个时候 NioEventLoop 中已经持有 selector 的实例了。

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

  1. protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
  2. boolean addTaskWakesUp, int maxPendingTasks,
  3. RejectedExecutionHandler rejectedHandler) {
  4. super(parent);
  5. this.addTaskWakesUp = addTaskWakesUp;
  6. this.maxPendingTasks = Math.max(16, maxPendingTasks);
  7. this.executor = ThreadExecutorMap.apply(executor, this);
  8. taskQueue = newTaskQueue(this.maxPendingTasks);
  9. rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
  10. }

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

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

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

  1. @Override
  2. public EventExecutorChooser newChooser(EventExecutor[] executors) {
  3. if (isPowerOfTwo(executors.length)) {
  4. return new PowerOfTwoEventExecutorChooser(executors);
  5. } else {
  6. return new GenericEventExecutorChooser(executors);
  7. }
  8. }

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

  1. private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {
  2. private final AtomicInteger idx = new AtomicInteger();
  3. private final EventExecutor[] executors;
  4. PowerOfTwoEventExecutorChooser(EventExecutor[] executors) {
  5. this.executors = executors;
  6. }
  7. @Override
  8. public EventExecutor next() {
  9. return executors[idx.getAndIncrement() & executors.length - 1];
  10. }
  11. }

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

NioSocketChannel 的初始化过程

我们先来看一看 NioSocketChannel 的继承结构图

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

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

  1. public B channel(Class<? extends C> channelClass) {
  2. if (channelClass == null) {
  3. throw new NullPointerException("channelClass");
  4. }
  5. return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
  6. }

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

  1. public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
  2. private final Constructor<? extends T> constructor;
  3. public ReflectiveChannelFactory(Class<? extends T> clazz) {
  4. ObjectUtil.checkNotNull(clazz, "clazz");
  5. try {
  6. this.constructor = clazz.getConstructor();
  7. } catch (NoSuchMethodException e) {
  8. }
  9. }
  10. @Override
  11. public T newChannel() {
  12. try {
  13. return constructor.newInstance();
  14. } catch (Throwable t) {
  15. throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);
  16. }
  17. }
  18. }

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

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

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

NioSocketChannel 实例化的时机

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

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

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

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

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

  1. final ChannelFuture initAndRegister() {
  2. Channel channel = null;
  3. try {
  4. // 发现在这里执行了实例化的操作
  5. channel = channelFactory.newChannel();
  6. init(channel);
  7. } catch (Throwable t) {
  8. }
  9. return regFuture;
  10. }

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

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

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

  1. public NioSocketChannel(SelectorProvider provider) {
  2. this(newSocket(provider));
  3. }
  4. private static SocketChannel newSocket(SelectorProvider provider) {
  5. try {
  6. // 终于发现了创建了一个 SocketChannel
  7. return provider.openSocketChannel();
  8. } catch (IOException e) {
  9. throw new ChannelException("Failed to open a socket.", e);
  10. }
  11. }

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

  1. public NioSocketChannel(SocketChannel socket) {
  2. this(null, socket);
  3. }
  4. public NioSocketChannel(Channel parent, SocketChannel socket) {
  5. super(parent, socket);
  6. config = new NioSocketChannelConfig(this, socket.socket());
  7. }

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

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

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

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

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

  1. /**
  2. * <em>Unsafe</em> operations that should <em>never</em> be called from user-code. These methods
  3. * are only provided to implement the actual transport, and must be invoked from an I/O thread except for the
  4. * following methods:
  5. * <ul>
  6. * <li>{@link #localAddress()}</li>
  7. * <li>{@link #remoteAddress()}</li>
  8. * <li>{@link #closeForcibly()}</li>
  9. * <li>{@link #register(EventLoop, ChannelPromise)}</li>
  10. * <li>{@link #deregister(ChannelPromise)}</li>
  11. * <li>{@link #voidPromise()}</li>
  12. * </ul>
  13. */
  14. interface Unsafe {
  15. RecvByteBufAllocator.Handle recvBufAllocHandle();
  16. SocketAddress localAddress();
  17. SocketAddress remoteAddress();
  18. void register(EventLoop eventLoop, ChannelPromise promise);
  19. void bind(SocketAddress localAddress, ChannelPromise promise);
  20. void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise);
  21. void disconnect(ChannelPromise promise);
  22. void close(ChannelPromise promise);
  23. void closeForcibly();
  24. void deregister(ChannelPromise promise);
  25. void beginRead();
  26. void write(Object msg, ChannelPromise promise);
  27. void flush();
  28. ChannelPromise voidPromise();
  29. ChannelOutboundBuffer outboundBuffer();
  30. }

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

Channel 的注册过程

上一步我们了解了 Channel 的初始化过程,下面这个 channel 什么时候注册到 Selector 中呢?

我们继续看跟进 bootstrap.connect() 代码。

发现最后跟进的到了 io.netty.bootstrap.Bootstrap#doResolveAndConnect() 方法中调用了内部的 initAndRegister(); 方法.

  1. private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
  2. final ChannelFuture regFuture = initAndRegister();
  3. return promise;
  4. }
  5. }

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

  1. final ChannelFuture initAndRegister() {
  2. Channel channel = null;
  3. try {
  4. channel = channelFactory.newChannel();
  5. init(channel);
  6. } catch (Throwable t) {
  7. }
  8. ChannelFuture regFuture = config().group().register(channel);
  9. if (regFuture.cause() != null) {
  10. if (channel.isRegistered()) {
  11. channel.close();
  12. } else {
  13. channel.unsafe().closeForcibly();
  14. }
  15. }
  16. return regFuture;
  17. }

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

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

  1. @Override
  2. public ChannelFuture register(Channel channel) {
  3. return next().register(channel);
  4. }

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

  1. @Override
  2. public ChannelFuture register(Channel channel) {
  3. return register(new DefaultChannelPromise(channel, this));
  4. }

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

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

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

  1. @Override
  2. public final void register(EventLoop eventLoop, final ChannelPromise promise) {
  3. AbstractChannel.this.eventLoop = eventLoop;
  4. if (eventLoop.inEventLoop()) {
  5. register0(promise);
  6. } else {
  7. try {
  8. eventLoop.execute(new Runnable() {
  9. @Override
  10. public void run() {
  11. register0(promise);
  12. }
  13. });
  14. } catch (Throwable t) {
  15. }
  16. }
  17. }

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

  1. private void register0(ChannelPromise promise) {
  2. try {
  3. if (!promise.setUncancellable() || !ensureOpen(promise)) {
  4. return;
  5. }
  6. boolean firstRegistration = neverRegistered;
  7. doRegister();
  8. neverRegistered = false;
  9. registered = true;
  10. pipeline.invokeHandlerAddedIfNeeded();
  11. safeSetSuccess(promise);
  12. pipeline.fireChannelRegistered();
  13. if (isActive()) {
  14. if (firstRegistration) {
  15. pipeline.fireChannelActive();
  16. } else if (config().isAutoRead()) {
  17. beginRead();
  18. }
  19. }
  20. } catch (Throwable t) {
  21. }
  22. }

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

  1. @Override
  2. protected void doRegister() throws Exception {
  3. boolean selected = false;
  4. for (;;) {
  5. try {
  6. selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
  7. return;
  8. } catch (CancelledKeyException e) {
  9. if (!selected) {
  10. eventLoop().selectNow();
  11. selected = true;
  12. } else {
  13. throw e;
  14. }
  15. }
  16. }
  17. }

终于我们看到了想要看的东西 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(); 方法.

  1. private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
  2. // 初始化并注册 channel
  3. final ChannelFuture regFuture = initAndRegister();
  4. // 获取到 channel
  5. final Channel channel = regFuture.channel();
  6. // 如果异步操作完成
  7. if (regFuture.isDone()) {
  8. if (!regFuture.isSuccess()) {
  9. return regFuture;
  10. }
  11. // 执行注册方法
  12. return doResolveAndConnect0(channel, remoteAddress, localAddress, channel.newPromise());
  13. } else {
  14. final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
  15. // 添加监听事件
  16. regFuture.addListener(new ChannelFutureListener() {
  17. @Override
  18. public void operationComplete(ChannelFuture future) throws Exception {
  19. Throwable cause = future.cause();
  20. if (cause != null) {
  21. promise.setFailure(cause);
  22. } else {
  23. promise.registered();
  24. // 执行注册方法
  25. doResolveAndConnect0(channel, remoteAddress, localAddress, promise);
  26. }
  27. }
  28. });
  29. return promise;
  30. }
  31. }

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

  1. private ChannelFuture doResolveAndConnect0(final Channel channel, SocketAddress remoteAddress,
  2. final SocketAddress localAddress, final ChannelPromise promise) {
  3. try {
  4. // 先获取到 eventLoop
  5. final EventLoop eventLoop = channel.eventLoop();
  6. // 封装一个 AddressResolver 对象
  7. final AddressResolver<SocketAddress> resolver = this.resolver.getResolver(eventLoop);
  8. if (!resolver.isSupported(remoteAddress) || resolver.isResolved(remoteAddress)) {
  9. doConnect(remoteAddress, localAddress, promise);
  10. return promise;
  11. }
  12. // 异步执行操作
  13. final Future<SocketAddress> resolveFuture = resolver.resolve(remoteAddress);
  14. if (resolveFuture.isDone()) {
  15. final Throwable resolveFailureCause = resolveFuture.cause();
  16. if (resolveFailureCause != null) {
  17. } else {
  18. // 重点
  19. doConnect(resolveFuture.getNow(), localAddress, promise);
  20. }
  21. return promise;
  22. }
  23. resolveFuture.addListener(new FutureListener<SocketAddress>() {
  24. @Override
  25. public void operationComplete(Future<SocketAddress> future) throws Exception {
  26. if (future.cause() != null) {
  27. channel.close();
  28. promise.setFailure(future.cause());
  29. } else {
  30. // 重点
  31. doConnect(future.getNow(), localAddress, promise);
  32. }
  33. }
  34. });
  35. } catch (Throwable cause) {
  36. promise.tryFailure(cause);
  37. }
  38. return promise;
  39. }

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

  1. private static void doConnect(
  2. final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise connectPromise) {
  3. // 获取对应的 NioSocketChannel 实例
  4. final Channel channel = connectPromise.channel();
  5. // 使用对应的 Loop 开启一个线程任务在线程中执行连接
  6. channel.eventLoop().execute(new Runnable() {
  7. @Override
  8. public void run() {
  9. if (localAddress == null) {
  10. // 使用 NioSocketChannel.connect()
  11. channel.connect(remoteAddress, connectPromise);
  12. } else {
  13. channel.connect(remoteAddress, localAddress, connectPromise);
  14. }
  15. connectPromise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
  16. }
  17. });
  18. }

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

  1. @Override
  2. public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
  3. return pipeline.connect(remoteAddress, promise);
  4. }
  5. @Override
  6. public final ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
  7. return tail.connect(remoteAddress, promise);
  8. }

我们继续跟进

  1. @Override
  2. public ChannelFuture connect(
  3. final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
  4. // 查找到对应的 handler
  5. final AbstractChannelHandlerContext next = findContextOutbound(MASK_CONNECT);
  6. EventExecutor executor = next.executor();
  7. if (executor.inEventLoop()) {
  8. next.invokeConnect(remoteAddress, localAddress, promise);
  9. } else {
  10. safeExecute(executor, new Runnable() {
  11. @Override
  12. public void run() {
  13. next.invokeConnect(remoteAddress, localAddress, promise);
  14. }
  15. }, promise, null);
  16. }
  17. return promise;
  18. }

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

之后根据查找出的 AbstractChannelHandlerContext 实例执行对应的 invokeConnect(); 方法参数是需要连接到的 ip, null, promise 实例中包含了 channel.

[外链图片转存失败(img-rEnlazj3-1565062020632)(:storage\54484bdb-a67b-434e-b759-cf0a98489245\b68bbeb1.png)]

  1. private void invokeConnect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
  2. if (invokeHandler()) {
  3. try {
  4. ((ChannelOutboundHandler) handler()).connect(this, remoteAddress, localAddress, promise);
  5. } catch (Throwable t) {
  6. notifyOutboundHandlerException(t, promise);
  7. }
  8. } else {
  9. connect(remoteAddress, localAddress, promise);
  10. }
  11. }

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

  1. @Override
  2. public void connect(
  3. ChannelHandlerContext ctx,
  4. SocketAddress remoteAddress, SocketAddress localAddress,
  5. ChannelPromise promise) {
  6. unsafe.connect(remoteAddress, localAddress, promise);
  7. }

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

  1. @Override
  2. public final void connect(
  3. final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
  4. if (!promise.setUncancellable() || !ensureOpen(promise)) {
  5. return;
  6. }
  7. try {
  8. if (connectPromise != null) {
  9. // Already a connect in process.
  10. throw new ConnectionPendingException();
  11. }
  12. boolean wasActive = isActive();
  13. // 执行连接操作
  14. if (doConnect(remoteAddress, localAddress)) {
  15. fulfillConnectPromise(promise, wasActive);
  16. } else {
  17. // 省略一些代码
  18. }
  19. } catch (Throwable t) {
  20. promise.tryFailure(annotateConnectException(t, remoteAddress));
  21. closeIfClosed();
  22. }
  23. }

在里面还调用了doConnect();

  1. @Override
  2. protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
  3. boolean success = false;
  4. try {
  5. // 使用工具类连接,传入 channel 和目标 ip
  6. boolean connected = SocketUtils.connect(javaChannel(), remoteAddress);
  7. if (!connected) {
  8. // 增加一个 CONNECT 事件
  9. selectionKey().interestOps(SelectionKey.OP_CONNECT);
  10. }
  11. success = true;
  12. return connected;
  13. } finally {
  14. if (!success) {
  15. doClose();
  16. }
  17. }
  18. }

我们来跟进 SocketUtils.connect();

  1. public static boolean connect(final SocketChannel socketChannel, final SocketAddress remoteAddress)
  2. throws IOException {
  3. try {
  4. // 不用做权限检查
  5. return AccessController.doPrivileged(new PrivilegedExceptionAction<Boolean>() {
  6. @Override
  7. public Boolean run() throws IOException {
  8. // 最终使用了 socketChannel.connect() 来连接到目标
  9. return socketChannel.connect(remoteAddress);
  10. }
  11. });
  12. } catch (PrivilegedActionException e) {
  13. throw (IOException) e.getCause();
  14. }
  15. }

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

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

Netty 源码学习——客户端流程分析的更多相关文章

  1. Netty 源码学习——EventLoop

    Netty 源码学习--EventLoop 在前面 Netty 源码学习--客户端流程分析中我们已经知道了一个 EventLoop 大概的流程,这一章我们来详细的看一看. NioEventLoopGr ...

  2. 【Netty源码学习】DefaultChannelPipeline(三)

    上一篇博客中[Netty源码学习]ChannelPipeline(二)我们介绍了接口ChannelPipeline的提供的方法,接下来我们分析一下其实现类DefaultChannelPipeline具 ...

  3. Netty源码学习系列之4-ServerBootstrap的bind方法

    前言 今天研究ServerBootstrap的bind方法,该方法可以说是netty的重中之重.核心中的核心.前两节的NioEventLoopGroup和ServerBootstrap的初始化就是为b ...

  4. 【Netty源码学习】ChannelPipeline(一)

    ChannelPipeline类似于一个管道,管道中存放的是一系列对读取数据进行业务操作的ChannelHandler. 1.ChannelPipeline的结构图: 在之前的博客[Netty源码学习 ...

  5. 【Netty源码学习】ServerBootStrap

    上一篇博客[Netty源码学习]BootStrap中我们介绍了客户端使用的启动服务,接下来我们介绍一下服务端使用的启动服务. 总体来说ServerBootStrap有两个主要功能: (1)调用父类Ab ...

  6. Netty源码解析—客户端启动

    Netty源码解析-客户端启动 Bootstrap示例 public final class EchoClient { static final boolean SSL = System.getPro ...

  7. HashMap的源码学习以及性能分析

    HashMap的源码学习以及性能分析 一).Map接口的实现类 HashTable.HashMap.LinkedHashMap.TreeMap 二).HashMap和HashTable的区别 1).H ...

  8. Dubbo源码学习--注册中心分析

    相关文章: Dubbo源码学习--服务是如何发布的 Dubbo源码学习--服务是如何引用的 注册中心 关于注册中心,Dubbo提供了多个实现方式,有比较成熟的使用zookeeper 和 redis 的 ...

  9. 【Netty源码学习】EventLoopGroup

    在上一篇博客[Netty源码解析]入门示例中我们介绍了一个Netty入门的示例代码,接下来的博客我们会分析一下整个demo工程运行过程的运行机制. 无论在Netty应用的客户端还是服务端都首先会初始化 ...

随机推荐

  1. 89、tensorflow使用GPU并行计算

    ''' Created on May 25, 2017 @author: p0079482 ''' # 分布式深度学习模型训练模式 # 在一台机器的多个GPU上并行训练深度学习模型 from date ...

  2. Missing artifact net.sf.json-lib:json-lib:jar:2.4

    Missing artifact net.sf.json-lib:json-lib:jar:2.4 出现上述这种错误就是JAR没有引入进来 这时候发现是因为JDK版本的问题,所以需要在加一句 < ...

  3. 18. HTTP协议一:概述、原理、版本、请求方法

    HTTP协议概述 HTTP协议就是我们常说的超文本协议(HyperText Transfer Protocol).HTTP协议是互联网上应用最为广泛的一种网络协议.所有的WWW文件都必须遵守这个标准. ...

  4. shell编程:定义函数

    第一种方式 function hello { echo "hello" } 第二种方式 hello() { echo "hello" } 调用函数 命令行:he ...

  5. Python中xlrd、xlwt、win32com模块对xls文件的读写操作

    # -*- coding: utf-8 -*- #xlrd和xlwt只支持xls文件读写,openpyxl只支持xlsx文件的读写操作 import xlrd import xlwt import w ...

  6. Matplotlib ValueError: _getfullpathname: embedded null character

    Traceback (most recent call last): File "<stdin>", line 1, in <module> File &q ...

  7. 子元素位于父元素中央 css实现

    wrap .box{ width: 200px; height:200px; background: pink; // 方案1 position: absolute; top:0; left:0; r ...

  8. Vue之自建管理后台(一)准备工作

    完成最基础的Vue环境及新建一个vue项目. 一般来说,我们拿到一个项目需求或者得到一个需求的时候,第一件应该做的事情不是立马坐在电脑前面去写代码,如果你这么做的,好吧...我只能暂时认定你为一个刚上 ...

  9. spring_AOP的注解开发

    logger日志类: package cn.mepu.utils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.la ...

  10. C#5.0 异步编程 Async和Await--理解异步方法与线程之间的关系

    这次来理解一下异步方法与线程之间的关系 新建一个控制台程序 代码如下 static void Main(string[] args) { Console.WriteLine("\n进入Mai ...