- 使用Netty必不可少的一个类:NioEventLoopGroup,它是一个处理I/O操作的多线程事件环,里面维护了多个EventLoop,当有事件发生的时候回选取其中一个EventLoop来处理这个事件,那么这个选取的算法是怎么实现的呢,其实很简单,是通过轮询来选取的。我们看一下Netty的源代码:
@Override
public EventExecutor next() {
return chooser.next();
}
那这个chooser是谁呢:
@SuppressWarnings("unchecked")
@Override
public EventExecutorChooser newChooser(EventExecutor[] executors) {
if (isPowerOfTwo(executors.length)) {
return new PowerOfTwoEventExecutorChooser(executors);
} else {
return new GenericEventExecutorChooser(executors);
}
}
可以看到根据条件的不同,会生成不同的chooser实例,executors就是NioEventLoopGroup里面维护的EventLoop,isPowerOfTwo方法把NioEventLoopGroup里面维护的EventLoop数量作为参数传递进去,我们看一下isPowerOfTwo这个方法的源码:
private static boolean isPowerOfTwo(int val) {
return (val & -val) == val;
}
代码很简单,但是是什么意思呀?经过我的几次测试后,大概了解到,这个方法是判断val是不是2的n次方,是,返回true,不是,返回false。下面来看下这两个chooser的源码有什么不一样
private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {
private final AtomicInteger idx = new AtomicInteger();
private final EventExecutor[] executors;
PowerOfTwoEventExecutorChooser(EventExecutor[] executors) {
this.executors = executors;
}
@Override
public EventExecutor next() {
return executors[idx.getAndIncrement() & executors.length - 1];
}
}
private static final class GenericEventExecutorChooser implements EventExecutorChooser {
private final AtomicInteger idx = new AtomicInteger();
private final EventExecutor[] executors;
GenericEventExecutorChooser(EventExecutor[] executors) {
this.executors = executors;
}
@Override
public EventExecutor next() {
return executors[Math.abs(idx.getAndIncrement() % executors.length)];
}
}
可以看到PowerOfTwoEventExecutorChooser ,这个类的next方法里面用到了位运算,我们都知道任何运算在计算机底层都要转换成位运算,所以直接位运算速度肯定要比一般的运算快。下面我试着揭秘一下为啥总数是2的n次方就可以使用这个位运算实现轮询算法。
通过这么个小小的轮询算法,我们可以看到netty真的是不放过一点可以提高性能的地方啊,所以我们在使用中可以尽可能的设置NioEventLoopGroup的线程数为2的n次方哦。