一、NioEventLoop工作過程
-
貼一段Netty Server端和Client端的demo
//1. Server端 public class NettyServerTest { public static void main(String[] args) { EventLoopGroup boss = new NioEventLoopGroup(1); EventLoopGroup worker = new NioEventLoopGroup(16); try { ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(boss, worker) .channel(NioServerSocketChannel.class) .handler(new ChannelHandler() { @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { System.out.println("executed method handlerAdded"); } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { } }) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8)) .addLast(new StringEncoder(CharsetUtil.UTF_8)) .addLast(new ChannelInBoundHandelerTest()) .addLast(new ChannelOutBoundHandelerTest1()) .addLast(new ChannelOutBoundHandelerTest2()); } }); ChannelFuture future = serverBootstrap.bind("127.0.0.1", 8081).sync(); future.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { boss.shutdownGracefully(); worker.shutdownGracefully(); } } }
//2. server端 ChannelHandler //(1) ChannelInboundHandler public class ChannelInBoundHandelerTest extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println(msg); ctx.writeAndFlush("NoDelay"); //當前InBound開始向尾結點傳播 ctx.fireChannelRead(msg); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { super.channelReadComplete(ctx); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } } //(2)ChannelOutboundHandler public class ChannelOutBoundHandelerTest1 extends ChannelOutboundHandlerAdapter { @Override public void read(ChannelHandlerContext ctx) throws Exception { super.read(ctx); System.out.println("ChannelOutBoundHandelerTest1"); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { super.exceptionCaught(ctx, cause); } } //(3)ChannelOutboundHandler public class ChannelOutBoundHandelerTest2 extends ChannelOutboundHandlerAdapter { @Override public void read(ChannelHandlerContext ctx) throws Exception { super.read(ctx); System.out.println("ChannelOutBoundHandelerTest2"); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { super.exceptionCaught(ctx, cause); } }
//3. Client public class NettyClientTest { public static void main(String[] args) { NioEventLoopGroup group = new NioEventLoopGroup(1); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8)) .addLast(new StringEncoder(CharsetUtil.UTF_8)) .addLast(new ClientChannelOutBoundHandelerTest()); } }); ChannelFuture future = bootstrap.connect("127.0.0.1", 8081).sync(); future.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { group.shutdownGracefully(); } } }
//4. Client //(1)ChannelInboundHandler public class ClientChannelOutBoundHandelerTest extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { ctx.writeAndFlush("Send message"); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println(msg); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { super.channelReadComplete(ctx); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { super.exceptionCaught(ctx, cause); } }
啓動服務端和客戶端
服務端結果
客戶端結果
ps:服務端多執行一次OutBound的1和2是因爲NioServerSocketChannel所在的NioEventLoop建立連接執行感興趣事件ACCPET時,創建NioSocketChannel並進行註冊,會調用AbstractChannel中register0方法的pipeline.fireChannelActive();從尾部節點開始調用OutBound~~~
-
NioEventLoop主要用於處理IO操作 + taskQueue中的任務 (也就是負責 IO線程 + 任務線程)
註冊時候會將NioEventLoop的線程啓動,會調用它的run方法。//1. NioEventLoop @Override protected void run() { for (;;) { try { //hasTasks() → 任務隊列和尾部隊列是否有任務 //有任務的話立即執行,並喚醒selector switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) { //默認不會有這個策略(沒看到返回這個策略的) case SelectStrategy.CONTINUE: continue; //阻塞去select case SelectStrategy.SELECT: //這裏解決了java臭名昭著的selector空輪詢導致CPU 100% //每次循環都把wakenUp設置成false //1.- (1) select(wakenUp.getAndSet(false)); if (wakenUp.get()) { //此方法耗性能 wakenUp.get()預判斷下,他可能是去執行task了而不是IO??? selector.wakeup(); } //不用阻塞且去執行任務了 default: // fallthrough } cancelledKeys = 0; needsToSelectAgain = false; final int ioRatio = this.ioRatio; if (ioRatio == 100) { try { processSelectedKeys(); } finally { // Ensure we always run tasks. runAllTasks(); } } else { final long ioStartTime = System.nanoTime(); try { //類似Nio中循環獲事件 processSelectedKeys(); } finally { // Ensure we always run tasks. final long ioTime = System.nanoTime() - ioStartTime; //執行非IO任務 //1 - (3) runAllTasks(ioTime * (100 - ioRatio) / ioRatio); } } } catch (Throwable t) { handleLoopException(t); } // Always handle shutdown even if the loop processing threw an exception. try { if (isShuttingDown()) { closeAll(); if (confirmShutdown()) { return; } } } catch (Throwable t) { handleLoopException(t); } } } //1.- (1) private void select(boolean oldWakenUp) throws IOException { Selector selector = this.selector; try { int selectCnt = 0; //當前系統的納秒數 long currentTimeNanos = System.nanoTime(); //當前時間+延時隊列第一個任務存活時間(存活時間還沒到就大於0) //delayNanos方法沒有延時任務返回1納秒 long selectDeadLineNanos = currentTimeNanos + delayNanos(currentTimeNanos); for (;;) { //延時執行任務是否到時間 long timeoutMillis = (selectDeadLineNanos - currentTimeNanos + 500000L) / 1000000L; //延時執行任務到時間了 if (timeoutMillis <= 0) { if (selectCnt == 0) { selector.selectNow(); selectCnt = 1; } break; } // If a task was submitted when wakenUp value was true, the task didn't get a chance to call // Selector#wakeup. So we need to check task queue again before executing select operation. // If we don't, the task might be pended until select operation was timed out. // It might be pended until idle timeout if IdleStateHandler existed in pipeline. //再次檢查 if (hasTasks() && wakenUp.compareAndSet(false, true)) { selector.selectNow(); selectCnt = 1; break; } //阻塞等待timeoutMillis毫秒數 int selectedKeys = selector.select(timeoutMillis); selectCnt ++; //還沒有selectedKeys , 沒被喚醒 , 沒有任務 , 沒有延時任務 繼續往下走 if (selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks() || hasScheduledTasks()) { // - Selected something, // - waken up by user, or // - the task queue has a pending task. // - a scheduled task is ready for processing break; } 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/2426 if (logger.isDebugEnabled()) { logger.debug("Selector.select() returned prematurely because " + "Thread.currentThread().interrupt() was called. Use " + "NioEventLoop.shutdownGracefully() to shutdown the NioEventLoop."); } selectCnt = 1; break; } long time = System.nanoTime(); //完整的執行完成了一個阻塞的select()操作 if (time - TimeUnit.MILLISECONDS.toNanos(timeoutMillis) >= currentTimeNanos) { // timeoutMillis elapsed without anything selected. selectCnt = 1; //是否循環了512次 } else 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); //epoll bug //1.- (2) rebuildSelector(); selector = this.selector; // Select again to populate selectedKeys. selector.selectNow(); selectCnt = 1; break; } currentTimeNanos = time; } if (selectCnt > MIN_PREMATURE_SELECTOR_RETURNS) { if (logger.isDebugEnabled()) { logger.debug("Selector.select() returned prematurely {} times in a row for Selector {}.", selectCnt - 1, selector); } } } catch (CancelledKeyException e) { if (logger.isDebugEnabled()) { logger.debug(CancelledKeyException.class.getSimpleName() + " raised by a Selector {} - JDK bug?", selector, e); } // Harmless exception - log anyway } } //1.- (2) public void rebuildSelector() { //不是當前線程放EventLoop中執行 if (!inEventLoop()) { execute(new Runnable() { @Override public void run() { rebuildSelector(); } }); return; } final Selector oldSelector = selector; final Selector newSelector; if (oldSelector == null) { return; } try { //新selector newSelector = openSelector(); } catch (Exception e) { logger.warn("Failed to create a new Selector.", e); return; } // Register all channels to the new Selector. int nChannels = 0; for (;;) { try { for (SelectionKey key: oldSelector.keys()) { //NioServerSocketChannel Object a = key.attachment(); try { if (!key.isValid() || key.channel().keyFor(newSelector) != null) { continue; } int interestOps = key.interestOps(); key.cancel(); //NioSocketChannel重新註冊到selector上 SelectionKey newKey = key.channel().register(newSelector, interestOps, a); if (a instanceof AbstractNioChannel) { // 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 instanceof AbstractNioChannel) { AbstractNioChannel ch = (AbstractNioChannel) a; ch.unsafe().close(ch.unsafe().voidPromise()); } else { @SuppressWarnings("unchecked") NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a; invokeChannelUnregistered(task, key, e); } } } } catch (ConcurrentModificationException e) { // Probably due to concurrent modification of the key set. continue; } break; } selector = newSelector; 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); } } } //1 - (3) protected boolean runAllTasks(long timeoutNanos) { //將到期延時任務加入任務隊列中 fetchFromScheduledTaskQueue(); //任務隊列取任務 Runnable task = pollTask(); if (task == null) { afterRunningAllTasks(); return false; } //timeoutNanos是任務能夠執行的時間 final long deadline = ScheduledFutureTask.nanoTime() + timeoutNanos; long runTasks = 0; long lastExecutionTime; for (;;) { safeExecute(task); runTasks ++; // Check timeout every 64 tasks because nanoTime() is relatively expensive. // XXX: Hard-coded value - will make it configurable if it is really a problem. //每執行64次判斷下時間,看是否執行任務時間超過了設定的比值 if ((runTasks & 0x3F) == 0) { lastExecutionTime = ScheduledFutureTask.nanoTime(); //執行任務超出了時間 直接跳出 避免影響IO操作 if (lastExecutionTime >= deadline) { break; } } task = pollTask(); if (task == null) { lastExecutionTime = ScheduledFutureTask.nanoTime(); break; } } afterRunningAllTasks(); this.lastExecutionTime = lastExecutionTime; return true; }