protectedvoidrun(){
// Netty解决办法具体步骤://// 1、先定义当前时间currentTimeNanos。// 2、接着计算出一个执行最少需要的时间timeoutMillis。// 3、每次对selectCnt做++操作。// 4、进行判断,如果到达执行到最少时间,则seletCnt重置为1。// 5、一旦到达SELECTOR_AUTO_REBUILD_THRESHOLD这个阀值,就需要重建selector来解决这个问题。// 6、这个阀值默认是512。int selectCnt =0;for(;;){
try{
int strategy;try{
// selectStrategy 终于要派上用场了// 它有两个值,一个是 CONTINUE 一个是 SELECT// 针对这块代码,我们分析一下。// 1. 如果 taskQueue 不为空,也就是 hasTasks() 返回 true,// 那么执行一次 selectNow(),该方法不会阻塞// 2. 如果 hasTasks() 返回 false,那么执行 SelectStrategy.SELECT 分支,// 进行 select(...),这块是带阻塞的// 这个很好理解,就是按照是否有任务在排队来决定是否可以进行阻塞
strategy = selectStrategy.calculateStrategy(selectNowSupplier,hasTasks());switch(strategy){
caseSelectStrategy.CONTINUE:continue;caseSelectStrategy.BUSY_WAIT:// fall-through to SELECT since the busy-wait is not supported with NIOcaseSelectStrategy.SELECT:long curDeadlineNanos =nextScheduledTaskDeadlineNanos();if(curDeadlineNanos ==-1L){
curDeadlineNanos =NONE;// nothing on the calendar}
nextWakeupNanos.set(curDeadlineNanos);try{
if(!hasTasks()){
strategy =select(curDeadlineNanos);}}finally{
// This update is just to help block unnecessary selector wakeups// so use of lazySet is ok (no race condition)
nextWakeupNanos.lazySet(AWAKE);}// fall throughdefault:}}catch(IOException e){
// If we receive an IOException here its because the Selector is messed up. Let's rebuild// the selector and retry. https://github.com/netty/netty/issues/8566rebuildSelector0();
selectCnt =0;handleLoopException(e);continue;}
selectCnt++;
cancelledKeys =0;
needsToSelectAgain =false;// 默认地,ioRatio 的值是 50finalint ioRatio =this.ioRatio;boolean ranTasks;if(ioRatio ==100){
try{
if(strategy >0){
processSelectedKeys();}}finally{
// Ensure we always run tasks.
ranTasks =runAllTasks();}}elseif(strategy >0){
// 如果 ioRatio 不是 100,那么根据 IO 操作耗时,限制非 IO 操作耗时finallong ioStartTime =System.nanoTime();try{
// 执行 IO 操作processSelectedKeys();}finally{
// 根据 IO 操作消耗的时间,计算执行非 IO 操作(runAllTasks)可以用多少时间.// Ensure we always run tasks.finallong ioTime =System.nanoTime()- ioStartTime;
ranTasks =runAllTasks(ioTime *(100- ioRatio)/ ioRatio);}}else{
ranTasks =runAllTasks(0);// This will run the minimum number of tasks}// 渠道任务设置 selectCnt 为 0if(ranTasks || strategy >0){
if(selectCnt >MIN_PREMATURE_SELECTOR_RETURNS&& logger.isDebugEnabled()){
logger.debug("Selector.select() returned prematurely {} times in a row for Selector {}.",
selectCnt -1, selector);}
selectCnt =0;// 避免jdk空轮询的bug}elseif(unexpectedSelectorWakeup(selectCnt)){
// Unexpected wakeup (unusual case)
selectCnt =0;}}catch(CancelledKeyException e){
// Harmless exception - log anywayif(logger.isDebugEnabled()){
logger.debug(CancelledKeyException.class.getSimpleName()+" raised by a Selector {} - JDK bug?",
selector, e);}}catch(Error e){
throw e;}catch(Throwable t){
handleLoopException(t);}finally{
// Always handle shutdown even if the loop processing threw an exception.try{
if(isShuttingDown()){
closeAll();if(confirmShutdown()){
return;}}}catch(Error e){
throw e;}catch(Throwable t){
handleLoopException(t);}}}}
解决空轮训cpu100%的bug
privatebooleanunexpectedSelectorWakeup(int selectCnt){
if(Thread.interrupted()){
// Thread was interrupted so reset selected keys and break so we not run into a busy loop.// As this is most likely a bug in the handler of the user or it's client library we will// also log it.//// See https://github.com/netty/netty/issues/2426if(logger.isDebugEnabled()){
logger.debug("Selector.select() returned prematurely because "+"Thread.currentThread().interrupt() was called. Use "+"NioEventLoop.shutdownGracefully() to shutdown the NioEventLoop.");}returntrue;}// select()没有阻塞立即返回了, 可能触发了空轮询, 这个值是512, 也就是说512次轮询的结果都为空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);// 把老的selectedKeys都注册到一个新的selector里面去, 并替换当前的selectorrebuildSelector();returntrue;}returnfalse;}
privatevoidrebuildSelector0(){
finalSelector oldSelector = selector;finalSelectorTuple newSelectorTuple;if(oldSelector ==null){
return;}try{
newSelectorTuple =openSelector();}catch(Exception e){
logger.warn("Failed to create a new Selector.", e);return;}// Register all channels to the new Selector.int nChannels =0;// 转移 SelectionKeyfor(SelectionKey key: oldSelector.keys()){
Object a = key.attachment();try{
if(!key.isValid()|| key.channel().keyFor(newSelectorTuple.unwrappedSelector)!=null){
continue;}int interestOps = key.interestOps();
key.cancel();SelectionKey newKey = key.channel().register(newSelectorTuple.unwrappedSelector, interestOps, a);if(a instanceofAbstractNioChannel){
// Update SelectionKey((AbstractNioChannel) a).selectionKey = newKey;}
nChannels ++;}catch(Exception e){
logger.warn("Failed to re-register a Channel to the new Selector.", e);if(a instanceofAbstractNioChannel){
AbstractNioChannel ch =(AbstractNioChannel) a;
ch.unsafe().close(ch.unsafe().voidPromise());}else{
@SuppressWarnings("unchecked")NioTask<SelectableChannel> task =(NioTask<SelectableChannel>) a;invokeChannelUnregistered(task, key, e);}}}// 设置 selector
selector = newSelectorTuple.selector;
unwrappedSelector = newSelectorTuple.unwrappedSelector;try{
// time to close the old selector as everything else is registered to the new one
oldSelector.close();}catch(Throwable t){
if(logger.isWarnEnabled()){
logger.warn("Failed to close the old Selector.", t);}}if(logger.isInfoEnabled()){
logger.info("Migrated "+ nChannels +" channel(s) to the new Selector.");}
privateChannelFuturedoBind(finalSocketAddress localAddress){
// 初始化 NioServerSocketChannel 或者 NioSocketChannel// 组装好 pipeline 中要添加的 ChannelHandler, 并回调对应的事件finalChannelFuture regFuture =initAndRegister();finalChannel channel = regFuture.channel();if(regFuture.cause()!=null){
return regFuture;}if(regFuture.isDone()){
// At this point we know that the registration was complete and successful.ChannelPromise promise = channel.newPromise();doBind0(regFuture, channel, localAddress, promise);return promise;}else{
// Registration future is almost always fulfilled already, but just in case it's not.finalPendingRegistrationPromise promise =newPendingRegistrationPromise(channel);
regFuture.addListener(newChannelFutureListener(){
@Overridepublicvoid