源码
启动流程
// Netty 使用 NioEventLoopGroup(NIO Boss 线程)来封装线程和 Selector
Selector selector = Selector.open();
// 创建 NioServerSocketChannel,同时会初始化它关联的 handler,以及为原生 ServerSocketChannel 存储 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);
io.netty.bootstrap.AbstractBootstrap.doBind
1、服务器启动入口
2、真正完成初始化、注册、绑定的方法
3、dobind 方法在主线程中执行
4、doBind 的两个重要方法
(1)initAndRegister:负责创建 NioServerSocketChannel、ServerSocketChannel(主线程中完成),注册 ServerSocketChannel(NIO 线程中完成)
(2)doBind0:负责连接的创建工作
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();
// 由NIO线程异步执行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;
}
}
initAndRegister
final ChannelFuture initAndRegister() {
Channel channel = null;
//init操作
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操作
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
// If we are here and the promise is not failed, it's one of the following cases:
// 1) If we attempted registration from the event loop, the registration has been completed at this point.
// i.e. It's safe to attempt bind() or connect() now because the channel has been registered.
// 2) If we attempted registration from the other thread, the registration request has been successfully
// added to the event loop's task queue for later execution.
// i.e. It's safe to attempt bind() or connect() now:
// because bind() or connect() will be executed *after* the scheduled registration task is executed
// because register(), bind(), and connect() are all bound to the same thread.
return regFuture;
}
1、init 操作
init主要完成以下三个操作
1、创建NioServerSocketChannel
2、通过NioServerSocketChannel构造器,创建ServerSocketChannel
3、由initChannel方法向NioServerSocketChannel中添加两个handler,添加操作在register之后被执行
(1)一个handler负责设置配置
(2)一个handler负责发生Accepet事件后建立连接
Channel channel = null;
try {
// 通过反射初始化NioServerSocketChannel
channel = channelFactory.newChannel();
init(channel);
}
(1)newChannel()
@Override
public T newChannel() {
try {
// 通过反射调用NioServerSocketChannel的构造方法
// 创建NioServerSocketChannel对象
return constructor.newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);
}
}
(2)constructor.newInstance():NioServerSocketChannel()
public NioServerSocketChannel() {
// 创建ServerSocketChannel实例
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
(3)newSocket(DEFAULT_SELECTOR_PROVIDER)
private static ServerSocketChannel newSocket(SelectorProvider provider) {
try {
// 原生ServerSocketChannel.open方法中:SelectorProvider.provider().openServerSocketChannel()
// 所以此处相当于ServerSocketChannel.open()
// 创建了ServerSocketChannel实例
return provider.openServerSocketChannel();
} catch (IOException e) {
throw new ChannelException("Failed to open a server socket.", e);
}
}
(4)init(Channel channel)
@Override
void init(Channel channel) {
……
// NioSocketChannl的Pipeline
ChannelPipeline p = channel.pipeline();
……
// 向Pipeline中添加一个handler,该handler等待被调用
//ChannelInitializer与其他Handler区别:只执行一次initChannel方法
p.addLast(new ChannelInitializer<Channel>() {
@Override
// register之后才调用该方法
public void initChannel(final Channel ch) {
final ChannelPipeline pipeline = ch.pipeline();
// 创建handler并加入到pipeline中
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
// 添加新的handler,在发生Accept事件后建立连接
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
2、register 操作
Register主要完成以下三个操作
1、完成主线程到NIO线程切换
(1)通过eventLoop.inEventLoop()进行线程判断,判断当前线程是否为NIO线程
(2)切换方式:eventLoop执行register操作
(3)register操作在NIO线程中完成
2、调用doRegister方法
(1)将ServerSocketChannel注册到EventLoop的Selector中
(2)此时还未关注事件
(3)添加NioServerSocketChannel附件
3、通过invokeHandlerAddedIfNeeded调用init中的initChannel方法
(1)initChannel方法主要创建两个handler
(2)一个handler负责设置配置
(3)一个handler负责发生Accept事件后建立连接
ChannelFuture regFuture = config().group().register(channel);
(1)register(channel)
@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
……
// 获取EventLoop
AbstractChannel.this.eventLoop = eventLoop;
// 此处完成了由 主线程 到 NIO线程 的切换
// eventLoop.inEventLoop():判断当前线程是否为NIO线程
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
// 首次执行时,启动EventLoop关联的NIO线程,向NIO线程中添加任务
eventLoop.execute(new Runnable() {
@Override
public void run() {
// 该方法中会执行doRegister
// 执行真正的注册操作
register0(promise);
}
});
} catch (Throwable t) {
……
}
}
}
(2)register0(promise)
private void register0(ChannelPromise promise) {
try {
……
// 执行真正的注册操作
doRegister();
neverRegistered = false;
registered = true;
// Ensure we call handlerAdded(...) before we actually notify the promise. This is needed as the
// user may already fire events through the pipeline in the ChannelFutureListener.
// 调用init中的initChannel方法
pipeline.invokeHandlerAddedIfNeeded();
//向Promise中设置执行成功的结果
safeSetSuccess(promise);
……
} catch (Throwable t) {
……
}
}
(3)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) {
……
}
}
}
(4)回调 initChannel
@Override
public void initChannel(final Channel ch) {
final ChannelPipeline pipeline = ch.pipeline();
// 创建handler并加入到pipeline中
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
// 添加新的handler,在发生Accept事件后建立连接
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
doBind0
1、绑定端口
(1)在 doRegister 和 invokeHandlerAddedIfNeeded 操作完成后,调用 safeSetSuccess(promise) 方法,向 Promise 中设置执行成功的结果
(2)此时 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();
// 如果没有异常,则执行绑定操作
doBind0(regFuture, channel, localAddress, promise);
}
}
});
(3)NioServerSocketChannel 的 doBind 方法
@SuppressJava6Requirement(reason = "Usage guarded by java version check")
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
//doBind0最底层调用原生ServerSocketChannel的bind方法,绑定端口
javaChannel().bind(localAddress, config.getBacklog());
} else {
javaChannel().socket().bind(localAddress, config.getBacklog());
}
}
2、关注事件
(1)在绑定端口操作完成后,会判断各种所有初始化操作是否已经完成,若完成,则会添加 ServerSocketChannel 感兴趣的事件
if (!wasActive && isActive()) {
invokeLater(new Runnable() {
@Override
public void run() {
//触发channel上pipeline所有handler的active事件
pipeline.fireChannelActive();
}
});
}
(2)最终在 AbstractNioChannel 的 doBeginRead 方法中,为 ServerSocketChannel 添加 Accept 事件
@Override
protected void doBeginRead() throws Exception {
// Channel.read() or ChannelHandlerContext.read() was called
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
//此处设置interestOps时使用的方法,避免覆盖关注的其他事件
//首先获取Channel所有感兴趣的事件
final int interestOps = selectionKey.interestOps();
// 如果ServerSocketChannel没有关注Accept事件
if ((interestOps & readInterestOp) == 0) {
// 则让其以添加的方式,关注Accepet事件
// readInterestOp 取值是 16
// 在 NioServerSocketChannel 创建时初始化
selectionKey.interestOps(interestOps | readInterestOp);
}
}
NioEventLoop
1、重要组成
(1)Selector
public final class NioEventLoop extends SingleThreadEventLoop {
……
// selector中的selectedKeys是基于数组的
// unwrappedSelector中的selectedKeys是基于HashSet的
private Selector selector;
private Selector unwrappedSelector;
private SelectedSelectionKeySet selectedKeys;
……
}
(2)Thread
public abstract class SingleThreadEventExecutor extends AbstractScheduledEventExecutor implements OrderedEventExecutor {
……
// 一个NioEventLoop对应一个线程
private volatile Thread thread;
// 执行器,单线程线程池,与thread为同一线程,但有更多功能
private final Executor executor;
……
}
(3)TaskQueue
public abstract class SingleThreadEventExecutor extends AbstractScheduledEventExecutor implements OrderedEventExecutor {
……
// 任务队列
private final Queue<Runnable> taskQueue;
……
}
public abstract class AbstractScheduledEventExecutor extends AbstractEventExecutor {
……
//定时任务队列
PriorityQueue<ScheduledFutureTask<?>> scheduledTaskQueue;
……
}
2、创建 Selector
(1)Selector 在 NioEventLoop 的构造方法中被创建
NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider, SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler, EventLoopTaskQueueFactory queueFactory) {
……
// 初始化selector,初始化过程在openSelector中
final SelectorTuple selectorTuple = openSelector();
this.selector = selectorTuple.selector;
this.unwrappedSelector = selectorTuple.unwrappedSelector;
}
(2)调用 openSelector() 方法, 该方法返回一个 SelectorTuple 对象,该方法是创建 Selector 的核心方法
private SelectorTuple openSelector() {
final Selector unwrappedSelector;
try {
// 等价于Selector.open()
// 创建unwrappedSelector对象
unwrappedSelector = provider.openSelector();
} catch (IOException e) {
throw new ChannelException("failed to open a new selector", e);
}
……
// 获得基于数组的selectedKeySet实现,由Netty提供
final SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();
Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
try {
// 通过反射拿获取nwrappedSelector中的selectedKeys、publicSelectedKeys属性
Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");
……
// 暴破反射,修改私有属性
Throwable cause = ReflectionUtil.trySetAccessible(selectedKeysField, true);
if (cause != null) {
return cause;
}
cause = ReflectionUtil.trySetAccessible(publicSelectedKeysField, true);
if (cause != null) {
return cause;
}
// unwrappedSelector的原生基于HashSet的SelectionKeySet,替换为基于数组的selectedKeySet
selectedKeysField.set(unwrappedSelector, selectedKeySet);
publicSelectedKeysField.set(unwrappedSelector, selectedKeySet);
return null;
} catch (NoSuchFieldException e) {
return e;
} catch (IllegalAccessException e) {
return e;
}
}
});
//private SelectedSelectionKeySet selectedKeys;
selectedKeys = selectedKeySet;
//new SelectedSelectionKeySetSelector(unwrappedSelector, selectedKeySet)
//通过Selector的构造方法获得Selector,其SelectionKeySet基于数组实现
return new SelectorTuple(unwrappedSelector,
new SelectedSelectionKeySetSelector(unwrappedSelector, selectedKeySet));
}
(3)SelectedSelectionKeySet 无参构造器,创建基于数组的 SelectionKey
SelectedSelectionKeySet() {
keys = new SelectionKey[1024];
}
(4)NioEventLoop 内部类:SelectorTuple
private static final class SelectorTuple {
final Selector unwrappedSelector;
final Selector selector;
SelectorTuple(Selector unwrappedSelector) {
this.unwrappedSelector = unwrappedSelector;
this.selector = unwrappedSelector;
}
//将该Selector的值赋给SelectorTuple类中的selector与unwrappedSelector
//通过SelectorTuple的构造方法,获得拥有两种Selector的SelectorTuple对象,并返回给NioEventLoop
SelectorTuple(Selector unwrappedSelector, Selector selector) {
this.unwrappedSelector = unwrappedSelector;
this.selector = selector;
}
}
两个 Selector
1、NioEventLoop 中有 selector 和 unwrappedSelector
2、区别:SelectedKeys 数据结构不同
(1)selector 中的 SelectedKeys 基于数组
(2)unwrappedSelector 中的 SelectedKeys 基于 HashSet
3、目的:数组的遍历效率要高于HashSet
NIO 线程启动时机
1、NioEventLoop 中的线程,在首次执行任务时,才会被创建,且只会被创建一次
2、SingleThreadEventExecutor.execute
@Override
public void execute(Runnable task) {
execute0(task);
}
private void execute0(@Schedule Runnable task) {
// 检测传入的任务是否为空,为空会抛出NullPointerException
ObjectUtil.checkNotNull(task, "task");
// 执行任务
// 此处判断了任务是否为懒加载任务,wakesUpForTask的返回值只会为true
execute(task, !(task instanceof LazyRunnable) && wakesUpForTask(task));
}
private void execute(Runnable task, boolean immediate) {
// 判断当前线程是否为NIO线程
// 判断方法为 return thread == this.thread;
// this.thread即为NIO线程,首次执行任务时,其为null
// thread为Thread.currentThread()
boolean inEventLoop = inEventLoop();
// 向任务队列taskQueue中添加任务
addTask(task);
// 当前线程不是NIO线程,则进入if语句
if (!inEventLoop) {
// 启动NIO线程的核心方法
startThread();
……
}
// 有任务需要被执行时,唤醒阻塞的NIO线程
if (!addTaskWakesUp && immediate) {
wakeup(inEventLoop);
}
}
private void startThread() {
// 查看NIO线程状态是否为未启动
// 该if代码块只会执行一次
// state一开始的值就是ST_NOT_STARTED
// private volatile int state = ST_NOT_STARTED;
if (state == ST_NOT_STARTED) {
// 通过原子属性更新器,将状态更新为启动(ST_STARTED)
if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
boolean success = false;
try {
//启动NIO线程,并执行任务
doStartThread();
success = true;
} finally {
if (!success) {
STATE_UPDATER.compareAndSet(this, ST_STARTED, ST_NOT_STARTED);
}
}
}
}
}
private void doStartThread() {
assert thread == null;
// 创建NIO线程并执行任务
executor.execute(new Runnable() {
@Override
public void run() {
// thread即为NIO线程,由当前线程赋值
thread = Thread.currentThread();
if (interrupted) {
thread.interrupt();
}
boolean success = false;
updateLastExecutionTime();
try {
//执行传入的任务
//该run方法是NioEvnetLoop的run方法
SingleThreadEventExecutor.this.run();
success = true;
}
……
});
}
进入 SELECT 分支阻塞
1、NioEvnetLoop 需要 I/O 事件、普通任务、定时任务
2、任务在 run 方法的 for 循环中
@Override
protected void run() {
int selectCnt = 0;
// 死循环,不断地从任务队列中获取各种任务来执行
for (;;) {
// 执行各种任务
……
}
}
3、该循环不会空转,执行到某些代码时,会被阻塞
4、run 方法的 switch 语句有多条分支
//具体执行分支的代码由 strategy 变量控制
int strategy;
strategy = selectStrategy.calculateStrategy(selectNowSupplier, hasTasks());
switch (strategy) {
……
}
5、strategy 值由 calculateStrategy 方法确定
@Override
public int calculateStrategy(IntSupplier selectSupplier, boolean hasTasks) throws Exception {
return hasTasks ? selectSupplier.get() : SelectStrategy.SELECT;
}
6、根据 hasTask 变量判断任务队列中是否有任务
private final IntSupplier selectNowSupplier = new IntSupplier() {
//若有任务,则通过selectSupplier获得strategy的值
public int get() throws Exception {
return NioEventLoop.this.selectNow();
}
};
int selectNow() throws IOException {
// selectSupplier.get() 底层是 selector.selectNow();
return this.selector.selectNow();
}
7、若没有任务,就会进入 SELECT 分支,使 NIO 线程阻塞
case SelectStrategy.SELECT:
long curDeadlineNanos = nextScheduledTaskDeadlineNanos();
if (curDeadlineNanos == -1L) {
curDeadlineNanos = NONE; // nothing on the calendar
}
nextWakeupNanos.set(curDeadlineNanos);
try {
if (!hasTasks()) {
// 执行select方法
strategy = select(curDeadlineNanos);
}
}
8、执行 NioEvnetLoop 的 select 方法,该方法内部会根据情况,执行 selector 有参 / 无参 select 方法
private int select(long deadlineNanos) throws IOException {
// 如果没有指定阻塞事件,就调用select()
if (deadlineNanos == NONE) {
return selector.select();
}
// 否则调用select(timeoutMillis),指定时间内未发生事件就停止阻塞
//超时时间(ms)=(截止时间(ns)+ 995000(ns))/ 1000000 = 截止时间(ms)+ 0.995(ms)
//若截止时间在 5 微秒以内,则超时时间为0
long timeoutMillis = deadlineToDelayNanos(deadlineNanos + 995000L) / 1000000L;
return timeoutMillis <= 0 ? selector.selectNow() : selector.select(timeoutMillis);
}
唤醒
1、select 方法阻塞线程,当没有 I/O 事件,但有其他任务需要执行时,需要唤醒线程
2、通过 execute 最后的 if 代码块完成唤醒
// 有任务需要被执行时,唤醒阻塞的NIO线程
if (!addTaskWakesUp && immediate) {
wakeup(inEventLoop);
}
3、NioEventLoop.wakeup 唤醒被 select 方法阻塞的 NIO 线程
@Override
protected void wakeup(boolean inEventLoop) {
//!inEventLoop:判断提交任务的是否为NIO线程
//若是其他线程,才能唤醒 NIO 线程
//若是 NIO 线程本身,则不能唤醒
//只有当其他线程给当前NIO线程提交任务时(如:执行execute),才会被唤醒
//nextWakeupNanos.getAndSet(AWAKE) != AWAKE
//通过AtomicLong进行更新,保证有多个线程同时提交任务时,只有一个线程能够唤醒NIO线程
if (!inEventLoop && nextWakeupNanos.getAndSet(AWAKE) != AWAKE) {
// 唤醒被selector.select方法阻塞的NIO线程
selector.wakeup();
}
}
Java NIO 在 Linux 系统下 epoll 空轮询问题
1、在 NioEventLoop 中,run 方法存在一个死循环,需要通过 select 方法阻塞线程
(1)但 select 方法存在 BUG,可能无法阻塞线程,导致循环一直执行,使得 CPU 负载升高
(2)Netty 中通过 selectCnt 变量,检测 select 方法是否发生空轮询 BUG
(3)若发生空轮询 BUG,那么 selectCnt 值的增长是十分迅速
@Override
protected void run() {
//Netty中通过selectCnt变量来检测select方法是否发生空轮询BUG
int selectCnt = 0;
for(;;){
……
// 可能发生空轮询,无法阻塞NIO线程
strategy = select(curDeadlineNanos);
……
//若发生空轮询BUG,那么selectCnt的值增长十分迅速
selectCnt++;
……
if(ranTasks || strategy > 0) {
……
} else if (unexpectedSelectorWakeup(selectCnt) ){
// 通过unexpectedSelectorWakeup方法中的rebuildSelector重建selector
// 并将selectCnt重置为0
selectCnt = 0;
}
}
}
2、通过 unexpectedSelectorWakeup 方法中的 rebuildSelector 重建 selector,并将 selectCnt 重置为 0
(1)selectCnt 大于等于 SELECTOR_AUTO_REBUILD_THRESHOLD(默认 512)时,Netty 则判断其出现空轮询 BUG,进行如下处理
(2)通过 rebuildSelector 方法重建 selector,将原 selector 配置信息传给新 selector,再用新selector 覆盖旧 selector,同时将 selectCnt 值设置为 0
if (SELECTOR_AUTO_REBUILD_THRESHOLD > 0 && selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) {
// The selector returned prematurely many times in a row.
// Rebuild the selector to work around the problem.
logger.warn("Selector.select() returned prematurely {} times in a row; rebuilding Selector {}.",selectCnt, selector);
// 重建selector,将原selector的配置信息传给新selector
// 再用新selector覆盖旧selector
rebuildSelector();
return true;
}
ioRatio
1、NioEventLoop 可以处理 I/O 事件、普通任务、定时任务,不同操作所耗费时间不同
2、通过 ioRatio 控制 NioEventLoop 处理 I/O 事件花费时间,占执行所有操作的总时间的比例
3、NioEventLoop.run()
// 处理IO事件时间比例,默认为50%
final int ioRatio = this.ioRatio;
……
// 判断IO事件时间比例设置是否为100%
if (ioRatio == 100) {
try {
// 若是,判断是否需要处理IO事件
if (strategy > 0) {
// 先处理IO事件
processSelectedKeys();
}
} finally {
//若否(或IO事件已经处理完毕),执行所有的普通任务和定时任务,直到所有任务都被处理完
// 确保总是在执行任务
// 剩下的时间都去处理普通任务和定时任务
ranTasks = runAllTasks();
}
//若ioRatio不为100
} else if (strategy > 0) { // 如果需要去处理IO事件
// 记录处理IO事件前的时间
final long ioStartTime = System.nanoTime();
try {
// 处理IO事件
processSelectedKeys();
} finally {
// 确保一直执行任务
// ioTime为处理IO事件所耗费的时间
final long ioTime = System.nanoTime() - ioStartTime;
// 计算处理其他任务的时间
// 超过设定的时间后,将会停止任务的执行,会在下一次循环中再继续执行
ranTasks = runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
} else { // 没有IO事件需要处理
// This will run the minimum number of tasks
// 直接处理普通和定时任务
ranTasks = runAllTasks(0);
}
处理事件
1、NioEventLoop.processSelectedKeys() 方法处理 I/O 事件
private void processSelectedKeys() {
// 若selectedKeys != null,说明selectedKeys基于数组实现
if (selectedKeys != null) {
// 处理各种IO事件
processSelectedKeysOptimized();
} else {
processSelectedKeysPlain(selector.selectedKeys());
}
}
2、processSelectedKeysOptimized()
private void processSelectedKeysOptimized() {
//for循环,遍历基于数组的SelectedKey
for (int i = 0; i < selectedKeys.size; ++i) {
// 获取SelectionKey
final SelectionKey k = selectedKeys.keys[i];
// null out entry in the array to allow to have it GC'ed once the Channel close
// See https://github.com/netty/netty/issues/2363
selectedKeys.keys[i] = null;
// 获取SelectionKey上的附件(其附件在register时添加),即NioServerSocketChannel
final Object a = k.attachment();
//如果附件继承自AbstractNioChannel
if (a instanceof AbstractNioChannel) {
// 处理事件,传入附件NioServerSocketChannel
processSelectedKey(k, (AbstractNioChannel) a);
} else {
@SuppressWarnings("unchecked")
NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
processSelectedKey(k, task);
}
if (needsToSelectAgain) {
// null out entries in the array to allow to have it GC'ed once the Channel close
// See https://github.com/netty/netty/issues/2363
selectedKeys.reset(i + 1);
selectAgain();
i = -1;
}
}
}
3、真正处理各种事件的方法:processSelectedKey,获取 SelectionKey 事件,然后进行相应处理
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
if (!k.isValid()) {
final EventLoop eventLoop;
try {
eventLoop = ch.eventLoop();
} catch (Throwable ignored) {
// If the channel implementation throws an exception because there is no event loop, we ignore this
// because we are only trying to determine if ch is registered to this event loop and thus has authority
// to close ch.
return;
}
// Only close ch if ch is still registered to this EventLoop. ch could have deregistered from the event loop
// and thus the SelectionKey could be cancelled as part of the deregistration process, but the channel is
// still healthy and should not be closed.
// See https://github.com/netty/netty/issues/5125
if (eventLoop == this) {
// close the channel if the key is not valid anymore
unsafe.close(unsafe.voidPromise());
}
return;
}
try {
int readyOps = k.readyOps();
// We first need to call finishConnect() before try to trigger a read(...) or write(...) as otherwise
// the NIO JDK channel implementation may throw a NotYetConnectedException.
//是否为连接事件
if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
// remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking
// See https://github.com/netty/netty/issues/924
int ops = k.interestOps();
ops &= ~SelectionKey.OP_CONNECT;
k.interestOps(ops);
unsafe.finishConnect();
}
// Process OP_WRITE first as we may be able to write some queued buffers and so free memory.
//是否为写事件
if ((readyOps & SelectionKey.OP_WRITE) != 0) {
// Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write
ch.unsafe().forceFlush();
}
// Also check for readOps of 0 to workaround possible JDK bug which may otherwise lead
// to a spin loop
//是否为读事件或接收事件
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}
} catch (CancelledKeyException ignored) {
unsafe.close(unsafe.voidPromise());
}
}
NIO 处理 Accept 事件
1、流程
(1)select() 阻塞线程,直到事件发生
(2)遍历 selectionKeys
(3)获取一个 key,判断事件类型是否为 Accept
(4)创建 SocketChannel,设置为非阻塞,并创建 NioSocketChannel
(5)将 SocketChannel 注册到 Selector 中,不关注事件,附件为 NioSocketChannel
(6)关注 SelectionKey 的 read 事件
2、SocketChannel 的创建与注册
(1)发生 Accept 事件后,会执行 NioEventLoop.run() 的 if 分支
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}
(2)NioMessageUnsafe.read()
public void read() {
……
try {
try {
do {
// doReadMessages中执行accept,获取SocketChannel,并创建NioSocketChannel,作为消息放入readBuf
// readBuf类型为ArrayList,作用:缓存消息
// private final List<Object> readBuf = new ArrayList<Object>();
int localRead = doReadMessages(readBuf);
……
// localRead值为1,表示一条消息,即接收一个客户端连接
allocHandle.incMessagesRead(localRead);
} while (allocHandle.continueReading());
} catch (Throwable t) {
exception = t;
}
int size = readBuf.size();
for (int i = 0; i < size; i ++) {
readPending = false;
// 让pipeline上的handler处理
// 触发ServerBootstrapAcceptor.channelRead
pipeline.fireChannelRead(readBuf.get(i));
}
……
} finally {
if (!readPending && !config.isAutoRead()) {
removeReadOp();
}
}
}
(3)NioSocketChannel.doReadMessages
@Override
protected int doReadMessages(List<Object> buf) throws Exception {
// 传入原生SocketChannel,处理accpet事件,创建SocketChannel,将其设置为非阻塞
SocketChannel ch = SocketUtils.accept(javaChannel());
try {
if (ch != null) {
// 创建NioSocketChannel,与原生SocketChannel建立联系,作为消息存入readBuf中
buf.add(new NioSocketChannel(this, ch));
return 1;
}
} catch (Throwable t) {
……
}
return 0;
}
(4)SocketUtils.accept
public static SocketChannel accept(final ServerSocketChannel serverSocketChannel) throws IOException {
try {
return AccessController.doPrivileged(new PrivilegedExceptionAction<SocketChannel>() {
@Override
public SocketChannel run() throws IOException {
// 处理accpet事件,创建SocketChannel
return serverSocketChannel.accept();
}
});
} catch (PrivilegedActionException e) {
throw (IOException) e.getCause();
}
}
(5)ServerBootstrapAcceptor.channelRead
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 此时msg为NioSocketChannel
final Channel child = (Channel) msg;
// NioSocketChannel添加childHandler,即初始化器
child.pipeline().addLast(childHandler);
// 设置选项
setChannelOptions(child, childOptions, logger);
for (Entry<AttributeKey<?>, Object> e: childAttrs) {
child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
}
try {
// 注册NioSocketChannel到新的NioEventLoop
childGroup.register(child).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
forceClose(child, future.cause());
}
}
});
} catch (Throwable t) {
forceClose(child, t);
}
}
(6)AbstractUnsafe.register 方法,将 SocketChannel 注册到 Selector 中,过程与启动流程中的register 过程类似
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
……
AbstractChannel.this.eventLoop = eventLoop;
//判断当前线程是否为EventLoop的线程
if (eventLoop.inEventLoop()) {
//若是,直接注册
register0(promise);
} else {
//若否,则封装为新任务,派发到新的EventLoop进行注册
try {
// 这行代码完成 NIO Boss 线程到 NIO Worker 线程的切换
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
} catch (Throwable t) {
……
}
}
}
(7)AbstractChannel.AbstractUnsafe.register0
private void register0(ChannelPromise promise) {
try {
……
// 该方法将SocketChannel注册到Selector中
doRegister();
//调用ChannelInitializer(NioSocketChannel的初始化器),添加Handler
//调用前:head -> ChannelInitializer -> tail
pipeline.invokeHandlerAddedIfNeeded();
//调用前:head -> 自定义Handler -> tail
safeSetSuccess(promise);
pipeline.fireChannelRegistered();
if (isActive()) {
if (firstRegistration) {
// 触发pipeline上active事件
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
beginRead();
}
}
} catch (Throwable t) {
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
(8)AbstractNioChannel.doRegister
@Override
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
// 将原生SocketChannel注册到当前EventLoop的Selector中,不关注任何事件,以当前NioSocketChannel作为附件
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
return;
} catch (CancelledKeyException e) {
……
}
}
}
(8)HeadContext.channelActive
public void channelActive(ChannelHandlerContext ctx) {
ctx.fireChannelActive();
// 关注read事件,此处NioSocketChannel的read,只为触发channel的事件注册,不涉及数据读取
readIfIsAutoRead();
}
(9)readIfIsAutoRead 进入 AbstractNioChannel.doBeginRead,通过该方法,SocketChannel 关注 read 事件
protected void doBeginRead() throws Exception {
// Channel.read() or ChannelHandlerContext.read() was called
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
// 此时interestOps为0
final int interestOps = selectionKey.interestOps();
if ((interestOps & readInterestOp) == 0) {
// 关注read事件
selectionKey.interestOps(interestOps | readInterestOp);
}
}
处理 Read 事件
1、流程
(1)select() 阻塞,指定事件发生
(2)遍历处理 selectedKeys
(3)获取 SelectionKey,判断事件类型是否为 read
(4)读取
2、发生 Read 事件后,会执行 NioEventLoop.run() 的 if 分支
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}
3、AbstractNioByteChannel.NioByteUnsafe.read()
@Override
public final void read() {
// 获得Channel的配置
final ChannelConfig config = config();
if (shouldBreakReadReady(config)) {
clearReadPending();
return;
}
final ChannelPipeline pipeline = pipeline();
// 根据配置创建ByteBufAllocator
final ByteBufAllocator allocator = config.getAllocator();
// 动态分配 byteBuf,IO操作使用直接内存,确定单次读取大小
final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle();
allocHandle.reset(config);
ByteBuf byteBuf = null;
boolean close = false;
try {
do {
// 创建ByteBuf
byteBuf = allocHandle.allocate(allocator);
// 读取内容,放入ByteBUf中
allocHandle.lastBytesRead(doReadBytes(byteBuf));
if (allocHandle.lastBytesRead() <= 0) {
byteBuf.release();
byteBuf = null;
close = allocHandle.lastBytesRead() < 0;
if (close) {
readPending = false;
}
break;
}
allocHandle.incMessagesRead(1);
readPending = false;
// 触发read事件,让pipeline上的handler处理
// 这时处理NioSocketChannel上的handler
pipeline.fireChannelRead(byteBuf);
byteBuf = null;
}
// 是否要继续循环
while (allocHandle.continueReading());
allocHandle.readComplete();
// 触发 read complete事件
pipeline.fireChannelReadComplete();
if (close) {
closeOnRead(pipeline);
}
} catch (Throwable t) {
handleReadException(pipeline, byteBuf, t, close, allocHandle);
} finally {
// Check if there is a readPending which was not processed yet.
// This could be for two reasons:
// * The user called Channel.read() or ChannelHandlerContext.read() in channelRead(...) method
// * The user called Channel.read() or ChannelHandlerContext.read() in channelReadComplete(...) method
//
// See https://github.com/netty/netty/issues/2254
if (!readPending && !config.isAutoRead()) {
removeReadOp();
}
}
}
4、DefaultMaxMessagesRecvByteBufAllocator.MaxMessageHandle.continueReading(io.netty.util.UncheckedBooleanSupplier)
public boolean continueReading(UncheckedBooleanSupplier maybeMoreDataSupplier) {
return
// 一般为true
config.isAutoRead() &&
// respectMaybeMoreData默认为true
// maybeMoreDataSupplier的逻辑是如果预期读取字节与实际读取字节相等,返回true
(!respectMaybeMoreData || maybeMoreDataSupplier.get()) &&
// 小于最大次数,maxMessagePerRead默认16
totalMessages < maxMessagePerRead &&
// 实际读到了数据
totalBytesRead > 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战