记录一次Netty源码追踪 探寻为什么Select的实现是KQueueSelectImpl。

发现问题

在netty启动的时候会涉及到如下的代码

ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
// 以下省略无关代码

netty的服务端会有两个线程组,第一个group用来处理连接,第二个group用来处理channel的IO事件。通过对一些例子的观察,大家都会声明一个NIO的线程组

  private final EventLoopGroup bossGroup = new NioEventLoopGroup();
  private final EventLoopGroup workerGroup = new NioEventLoopGroup();

查看其源码,大多数都是调用父类的构造器,其之所以为NIO类型是使用了NioEventLoop。
而Nio必定会有个Select方法,在select方法中调用的是操作系统的方法。在这里插入图片描述
这里就是NioEventLoop封装的select方法,就拿selectNow()方法来说,进一步进入源码,selectNow是Selector的一个抽象方法,Selector有一个默认的实现SelectorImpl


在内部有一个实现

 protected abstract int doSelect(long var1) throws IOException;

    private int lockAndDoSelect(long var1) throws IOException {
        synchronized(this) {
            if (!this.isOpen()) {
                throw new ClosedSelectorException();
            } else {
                int var10000;
                synchronized(this.publicKeys) {
                    synchronized(this.publicSelectedKeys) {
                        var10000 = this.doSelect(var1); // 最终的调用
                    }
                }

                return var10000;
            }
        }
    }

其最终调用的还是doSelect方法,而SelectorImpl有三个类继承了它,所以该方法的实现取决于最初的select的实现是哪一个。接下来就很好奇到底会是哪一个?

源码查看

其实通过断点可以直接看到最终的结果是KQueueSelector类型

但是为什么就是这个呢? 在哪里可以查看到?在NioEventLoop中初始化时,其实是对selector进行了赋值,初始化代码如下。

  NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
                 SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
        super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
        if (selectorProvider == null) {
            throw new NullPointerException("selectorProvider");
        }
        if (strategy == null) {
            throw new NullPointerException("selectStrategy");
        }
        provider = selectorProvider;
        final SelectorTuple selectorTuple = openSelector();
        selector = selectorTuple.selector; // 对selector进行赋值
        unwrappedSelector = selectorTuple.unwrappedSelector;
        selectStrategy = strategy;
    }

可以看到这里主要通过了selectorTuple进行获取,而selectorTuple是通过了openSelector()方法。
在这里插入图片描述
openSelector()方法主要由provider进行提供selector生成。这里可以看下上面的NioEventLoop构造器方法,这里的provider是由外部参数传过来的!!!
接下来通过方法栈一步步查看之前调用的方法,这里使用的是IDEA编辑器, 断点在NioEventLoop的构造器方法。
在这里插入图片描述
这里可以看到前一个调用者是NioEventLoopGroup,provider参数是包含在arg… 里面的,然后接下去找直到找到了下面的方法。这个是NioEventLoopGroup的重载构造器,这里调用了 SelectorProvider.provider()来生成provider
在这里插入图片描述
进入内部的源码可以看到主要调用的是源码内定义好的默认方法,这里有从系统参数获取的,也有作为服务获取的,最终调用的就是默认方法

public static SelectorProvider provider() {
        synchronized (lock) {
            if (provider != null)
                return provider;
            return AccessController.doPrivileged(
                new PrivilegedAction<SelectorProvider>() {
                    public SelectorProvider run() {
                            if (loadProviderFromProperty())
                                return provider;
                            if (loadProviderAsService())
                                return provider;// 方法调用的地方
                            provider = sun.nio.ch.DefaultSelectorProvider.create(); 
                            return provider;
                        }
                    });
        }
    }

点击默认方法查看实现

结果已经很明显了,我们什么都没有设置,这里使用了默认的方法,由于这次代码的追踪是mac上的可能在windows上会有略微差别。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值