一、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; }