【Netty 源码分析】

一、启动流程


Netty启动流程可以简化成如下代码

// netty 中使用 NioEventLoopGroup (简称 nio boss 线程)来封装线程和 selector
Selector selector = Selector.open(); 

// 创建 NioServerSocketChannel,同时会初始化它关联的 handler,以及为原生 ssc 存储 config
NioServerSocketChannel attachment = new NioServerSocketChannel();

// 创建 NioServerSocketChannel 时,创建了 java 原生的 ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); 
serverSocketChannel.configureBlocking(false);

// 启动 nio boss 线程执行接下来的操作

//注册(仅关联 selector 和 NioServerSocketChannel),未关注事件
SelectionKey selectionKey = serverSocketChannel.register(selector, 0, attachment);

// head -> 初始化器 -> ServerBootstrapAcceptor -> tail,初始化器是一次性的,只为添加 acceptor

// 绑定端口
serverSocketChannel.bind(new InetSocketAddress(8080));

// 触发 channel active 事件,在 head 中关注 op_accept 事件
selectionKey.interestOps(SelectionKey.OP_ACCEPT);
  • 获得选择器Selector,Netty中使用NioEventloopGroup中的NioEventloop封装了线程和选择器
  • 创建NioServerSocketChannel,该Channel作为附件添加到ServerSocketChannel中
  • 创建ServerSocketChannel,将其设置为非阻塞模式,并注册到Selector中,此时未关注事件,但是添加了附件NioServerSocketChannel
  • 绑定端口
  • 通过interestOps设置感兴趣的事件

ssc : 其实就是 ServerSocketChannel

流程:

  • 1、init main
    • 创建NioServerSocketChannel main
      • 内部其实就是new出来,底层是一个newSocket过程
      • return provider.openServerSocketChannel();
    • add NioServerSocketChannel 初始化handler main
      • 初始化handler 等待调用 (main 未调用, nio-thread调用)

向nio ssc 加入了accept handler (在accept事件发生后建立连接)

  • 2、register (切换线程)
    • 启动 nio boss 线程 main
    • 原生 ssc 注册到 selector 未关注事件 nio-thread
    • 执行 NioServerSocketChannel 初始化 handler nio-thread
  • 3、regFuture 等待回调 dobind0 nio-thread
    • 原生 ServerSocketChannel 绑定 nio-thread
    • 触发 NioServerSocketChannel active 事件 nio-thread

bind
选择器Selector的创建是在NioEventLoopGroup中完成的。
NioServerSocketChannel 与 ServerSocketChannel的创建、ServerSocketChannel注册到Seletor中以及绑定操作都是由 bind 方法完成
**服务器入口: ** io.netty.bootstrap.AbstractBootstrap#bind(int)

 /**
     * Create a new {@link Channel} and bind it.
     */
    public ChannelFuture bind(SocketAddress localAddress) {
   
   
        validate();
        return doBind(ObjectUtil.checkNotNull(localAddress, "localAddress"));
    }

doBind

真正完成初始化、注册以及绑定的方法是
io.netty.bootstrap.AbstractBootstrap#doBind
注意: dobind方法在主线程中执行

private ChannelFuture doBind(final SocketAddress localAddress) {
   
   
    // 负责NioServerSocketChannel和ServerSocketChannel的创建
    // ServerSocketChannel的注册工作
    // init由main线程完成,regisetr由NIO线程完成
    final ChannelFuture regFuture = initAndRegister();
    final Channel channel = regFuture.channel();
    if (regFuture.cause() != null) {
   
   
        return regFuture;
    }

    // 因为register操作是异步的
    // 所以要判断主线程执行到这里时,register操作是否已经执行完毕
    if (regFuture.isDone()) {
   
   
        // At this point we know that the registration was complete and successful.
        ChannelPromise promise = channel.newPromise();
        
        // 执行doBind0绑定操作
        doBind0(regFuture, channel, localAddress, promise);
        return promise;
    } else {
   
   
        // Registration future is almost always fulfilled already, but just in case it's not.
        // 如果register操作还没执行完,就会到这个分支中来
        final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
        
        // 添加监听器,NIO线程异步进行doBind0操作
        regFuture.addListener(new ChannelFutureListener() {
   
   
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
   
   
                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;
    }
}
  • doBind()中有两个重要方法initAndRegister()和doBind0(regFuture, channel, localAddress, promise)
  • initAndRegister主要负责NioServerSocketChannel和ServerSocketChannel的创建(主线程中完成)与ServerSocketChannel注册(NIO线程中完成)工作
  • doBind0则负责连接的创建工作

initAndRegisterd

final ChannelFuture initAndRegister() {
   
   
    Channel channel = null;
    try {
   
   
        channel = channelFactory.newChannel();
        init(channel);
    } catch (Throwable t) {
   
   
        if (channel != null) {
   
   
            // channel can be null if newChannel crashed (eg SocketException("too many open files"))
            channel.unsafe().closeForcibly();
            // 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);
    }

    
    // register promise.channel().unsafe().register(this, promise)方法
    ChannelFuture regFuture = config().group().register(channel);
    if (regFuture.cause() != null) {
   
   
        if (channel.isRegistered()) {
   
   
            channel.close();
        } else {
   
   
            channel.unsafe().closeForcibly();
        }
    }
    return regFuture;
}


register:
//promise.channel().unsafe().register(this, promise)方法

@Override
        public final void register(EventLoop eventLoop, final ChannelPromise promise) {
   
   
            ...
            
            AbstractChannel.this.eventLoop = eventLoop;

            // 此处完成了 main 到nio 线程的切换
            // 当前线程是否是nio线程
            if (eventLoop.inEventLoop()) {
   
   
                register0(promise);
            } else {
   
   
                try {
   
   
                    // 向nio线程中添加任务
                    eventLoop.execute(new Runnable() {
   
   
                        @Override
                        public void run() {
   
   
                            // 该方法中会执行doRegister
                            // 执行真正的注册操作
                            register0(promise);
                        }
                    });
                } catch (Throwable t) {
   
   
                  
                }
            }
        }
register0
private void register0(ChannelPromise promise) {
   
   
            try {
   
   
                // check if the channel is still open as it could be closed in the mean time when the register
                // call was outside of the eventLoop
                if (!promise.setUncancellable() || !ensureOpen(promise)) {
   
   
                    return;
                }
                boolean firstRegistration = neverRegistered;
                doRegister();
                neverRegistered = false;
                registered = true;
                
                // 调用init中的initChannel方法  其实就是第一点中未执行的加handler
                pipeline.invokeHandlerAddedIfNeeded();
            }
doRegister方法
@Override
protected void doRegister() throws Exception {
   
   
    boolean selected = false;
    for (;;) {
   
   
        try {
   
   
            // javaChannel()即为ServerSocketChannel
            // eventLoop().unwrappedSelector()获取eventLoop中的Selector
            // this为NIOServerSocketChannel,作为附件
            selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
            return;
        } catch (CancelledKeyException e) {
   
   
            ...
           
        }
    }
}

doBind0

绑定端口

在doRegister和invokeHandlerAddedIfNeeded操作中的完成后,会调用safeSetSuccess(promise)方法,向Promise中设置执行成功的结果。此时doBind方法中由initAndRegister返回的ChannelFuture对象regFuture便会由NIO线程异步执行doBind0绑定操作

// initAndRegister为异步方法,会返回ChannelFuture对象
final ChannelFuture regFuture = initAndRegister();
regFuture.addListener(new ChannelFutureListener() {
   
   
    @Override
    public void operationComplete(ChannelFuture future) throws Exception {
   
   
        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(
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

下次遇见说你好

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值