发现问题
在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上会有略微差别。