Netty源码分析-客户端启动流程

上接Netty源码分析之服务端启动流程,本节看下客户端启动的流程。

public class Client {
    private static final Logger LOGGER = LoggerFactory.getLogger(Client.class);
    private static String host = "127.0.0.1";
    private static int port = 9090;

    public static void main(String[] args) {
        NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(eventLoopGroup)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        socketChannel.pipeline().addLast(new ClientChannelHandler());
                    }
                }).option(ChannelOption.TCP_NODELAY, true);

        try {
            bootstrap.connect(host, port).sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

NioEventLoopGroup和Bootstrap的初始化与服务端基本一致,重点关注下connect的流程。

public ChannelFuture connect(SocketAddress remoteAddress) {
    if (remoteAddress == null) {
        throw new NullPointerException("remoteAddress");
    } else {
        this.validate();
        return this.doResolveAndConnect(remoteAddress, this.config.localAddress());
    }
}

核心逻辑在doResolveAndConnect

private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
    ChannelFuture regFuture = this.initAndRegister();
    final Channel channel = regFuture.channel();
    if (regFuture.isDone()) {
        return !regFuture.isSuccess() ? regFuture : this.doResolveAndConnect0(channel, remoteAddress, localAddress, channel.newPromise());
    } else {
        final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
        regFuture.addListener(new ChannelFutureListener() {
            public void operationComplete(ChannelFuture future) throws Exception {
                Throwable cause = future.cause();
                if (cause != null) {
                    promise.setFailure(cause);
                } else {
                    promise.registered();
                    Bootstrap.this.doResolveAndConnect0(channel, remoteAddress, localAddress, promise);
                }

            }
        });
        return promise;
    }
}

1. this.initAndRegister

  • 通过反射创建NioSocketChannel对象。
  • 和NioServerSocketChannel端一样,NioSocketChannel实例化的时候内部会创建unsafe和pipeline对象,但是客户端unsafe的实例对象是NioSocketChannelUnsafe(前面提过,unsafe可以看作是Netty调用底层NIO的桥梁
  • 注册SelectionKey.OP_READ事件
final ChannelFuture initAndRegister() {
    Channel channel = null;

    try {
        channel = this.channelFactory.newChannel();
        this.init(channel);
    } catch (Throwable var3) {
        if (channel != null) {
            channel.unsafe().closeForcibly();
            return (new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE)).setFailure(var3);
        }

        return (new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE)).setFailure(var3);
    }

    ChannelFuture regFuture = this.config().group().register(channel);
    if (regFuture.cause() != null) {
        if (channel.isRegistered()) {
            channel.close();
        } else {
            channel.unsafe().closeForcibly();
        }
    }

    return regFuture;
}
从eventLooGroup选择一个线程处理channel注册,channel和当前线程被封装成了DefaultChannelPromise对象。

public ChannelFuture register(Channel channel) {
    return this.register((ChannelPromise)(new DefaultChannelPromise(channel, this)));
}

启动线程执行注册AbstractUnsafe.this.register0(promise)

AbstractChannel.this.eventLoop = eventLoop;
if (eventLoop.inEventLoop()) {
    this.register0(promise);
} else {
    try {
        eventLoop.execute(new Runnable() {
            public void run() {
                AbstractUnsafe.this.register0(promise);
            }
        });
    } catch (Throwable var4) {
        AbstractChannel.logger.warn("Force-closing a channel whose registration task was not accepted by an event loop: {}", AbstractChannel.this, var4);
        this.closeForcibly();
        AbstractChannel.this.closeFuture.setClosed();
        this.safeSetFailure(promise, var4);
    }
}
  • AbstractChannel.this.doRegister() 将当前socketChannel注册到selector上
  • AbstractChannel.this.pipeline.fireChannelRegistered()在链表上传播register事件
private void register0(ChannelPromise promise) {
    try {
        if (!promise.setUncancellable() || !this.ensureOpen(promise)) {
            return;
        }

        boolean firstRegistration = this.neverRegistered;
        AbstractChannel.this.doRegister();
        this.neverRegistered = false;
        AbstractChannel.this.registered = true;
        AbstractChannel.this.pipeline.invokeHandlerAddedIfNeeded();
        this.safeSetSuccess(promise);
        AbstractChannel.this.pipeline.fireChannelRegistered();
        if (AbstractChannel.this.isActive()) {
            if (firstRegistration) {
                AbstractChannel.this.pipeline.fireChannelActive();
            } else if (AbstractChannel.this.config().isAutoRead()) {
                this.beginRead();
            }
        }
    } catch (Throwable var3) {
        this.closeForcibly();
        AbstractChannel.this.closeFuture.setClosed();
        this.safeSetFailure(promise, var3);
    }

}

2. doResolveAndConnect

再回到Bootstrap,跟进doResolveAndConnect0方法

if (regFuture.isDone()) {
    return !regFuture.isSuccess() ? regFuture : this.doResolveAndConnect0(channel, remoteAddress, localAddress, channel.newPromise());
} 

执行连接操作

doConnect((SocketAddress)resolveFuture.getNow(), localAddress, promise);
从链表尾部开始传播connect事件,依次调用Outbound为True的Handler
public final ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
    return this.tail.connect(remoteAddress, promise);
}

最终调用了unsafe.connect方法

public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception {
    this.unsafe.connect(remoteAddress, localAddress, promise);
}

3. 服务端处理

connect完成之后,再来关注下服务端AbstractChannel.register0方法。

由于首次注册,会进入AbstractChannel.this.pipeline.fireChannelActive()

private void register0(ChannelPromise promise) {
    try {
        if (!promise.setUncancellable() || !this.ensureOpen(promise)) {
            return;
        }

        boolean firstRegistration = this.neverRegistered;
        AbstractChannel.this.doRegister();
        this.neverRegistered = false;
        AbstractChannel.this.registered = true;
        AbstractChannel.this.pipeline.invokeHandlerAddedIfNeeded();
        this.safeSetSuccess(promise);
        AbstractChannel.this.pipeline.fireChannelRegistered();
        if (AbstractChannel.this.isActive()) {
            if (firstRegistration) {
                AbstractChannel.this.pipeline.fireChannelActive();
            } else if (AbstractChannel.this.config().isAutoRead()) {
                this.beginRead();
            }
        }
    } catch (Throwable var3) {
        this.closeForcibly();
        AbstractChannel.this.closeFuture.setClosed();
        this.safeSetFailure(promise, var3);
    }

}

链表上调用inbound为ture的Handler处理active事件

private void invokeChannelActive() {
    if (this.invokeHandler()) {
        try {
            ((ChannelInboundHandler)this.handler()).channelActive(this);
        } catch (Throwable var2) {
            this.notifyHandlerException(var2);
        }
    } else {
        this.fireChannelActive();
    }

}
完成激活后,会调用readIfIsAutoRead
public void channelActive(ChannelHandlerContext ctx) throws Exception {
    ctx.fireChannelActive();
    this.readIfIsAutoRead();
}

从tail向head传播read事件

public final ChannelPipeline read() {
    this.tail.read();
    return this;
}

最终调用unsafe的beginRead方法,等待客户端数据到来

public void read(ChannelHandlerContext ctx) {
    this.unsafe.beginRead();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值