Netty源码解析系列二:Netty请求的处理流程

本文详细解析了Netty请求的处理流程,包括服务端绑定端口、客户端连接、NioEventLoop的角色以及连接建立后的读写操作。重点介绍了NioEventLoop如何处理连接和事件,以及解决CPU空轮训问题。同时,概述了从注册事件到读写操作的完整步骤,涵盖了服务端和客户端的不同处理阶段。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Netty请求的处理流程

简单使用

服务端绑定端口并处理请求

public class NettyServer {
   
   

    public static void main(String[] args) {
   
   
        new NettyServer().bing(7397);
    }

    private void bing(int port) {
   
   
        //配置服务端NIO线程组
        EventLoopGroup parentGroup = new NioEventLoopGroup(1); //NioEventLoopGroup extends MultithreadEventLoopGroup Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
        EventLoopGroup childGroup = new NioEventLoopGroup();
        try {
   
   
            ServerBootstrap b = new ServerBootstrap();
            b.group(parentGroup, childGroup)
                    .channel(NioServerSocketChannel.class)    //非阻塞模式
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
   
   
                        @Override
                        protected void initChannel(SocketChannel channel) throws Exception {
   
   
                            //对象传输处理
                            channel.pipeline().addLast(new ObjDecoder(MsgInfo.class));
                            channel.pipeline().addLast(new ObjEncoder(MsgInfo.class));
                            // 在管道中添加我们自己的接收数据实现方法
                            channel.pipeline().addLast(new LoggingHandler());
                            channel.pipeline().addLast(new MyServerHandler());
                        }
                    });
            ChannelFuture f = b.bind(port).sync();
            System.out.println(" demo-netty server start done. ");
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
   
   
            e.printStackTrace();
        } finally {
   
   
            childGroup.shutdownGracefully();
            parentGroup.shutdownGracefully();
        }

    }

}

客户端连接服务端

public class NettyClient {
   
   

    public static void main(String[] args) {
   
   
        new NettyClient().connect("127.0.0.1", 7397);
    }

    private void connect(String inetHost, int inetPort) {
   
   
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
   
   
            Bootstrap b = new Bootstrap();
            b.group(workerGroup);
            b.channel(NioSocketChannel.class);
            b.option(ChannelOption.AUTO_READ, true);
            b.handler(new ChannelInitializer<SocketChannel>() {
   
   
                @Override
                protected void initChannel(SocketChannel channel) throws Exception {
   
   
                    //对象传输处理
                    channel.pipeline().addLast(new ObjDecoder(MsgInfo.class));
                    channel.pipeline().addLast(new ObjEncoder(MsgInfo.class));
                    // 在管道中添加我们自己的接收数据实现方法
                    channel.pipeline().addLast(new MyClientHandler());
                }
            });
            ChannelFuture f = b.connect(inetHost, inetPort).sync();
            System.out.println("demo-netty client start done.");

            f.channel().writeAndFlush(MsgUtil.buildMsg(f.channel().id().toString(), "你好,这里是客户端发来的消息1"));
            f.channel().write(MsgUtil.buildMsg(f.channel().id().toString(), "你好,这里是客户端发来的消息2"));

            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
   
   
            e.printStackTrace();
        } finally {
   
   
            workerGroup.shutdownGracefully();
        }
    }

}

NioEventLoop进行的操作

处理连接以及处理事件

protected void run() {
   
   
    // 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) {
   
   
                case SelectStrategy.CONTINUE:
                    continue;

                case SelectStrategy.BUSY_WAIT:
                    // fall-through to SELECT since the busy-wait is not supported with NIO

                case SelectStrategy.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 through
                default:
                }
            } 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/8566
                rebuildSelector0();
                selectCnt = 0;
                handleLoopException(e);
                continue;
            }

            selectCnt++;
            cancelledKeys = 0;
            needsToSelectAgain = false;
            // 默认地,ioRatio 的值是 50
            final int ioRatio = this.ioRatio;
            boolean ranTasks;
            if (ioRatio == 100) {
   
   
                try {
   
   
                    if (strategy > 0) {
   
   
                        processSelectedKeys();
                    }
                } finally {
   
   
                    // Ensure we always run tasks.
                    ranTasks = runAllTasks();
                }
            } else if (strategy > 0) {
   
   
                // 如果 ioRatio 不是 100,那么根据 IO 操作耗时,限制非 IO 操作耗时
                final long ioStartTime = System.nanoTime();
                try {
   
   
                    // 执行 IO 操作
                    processSelectedKeys();
                } finally {
   
   
                    // 根据 IO 操作消耗的时间,计算执行非 IO 操作(runAllTasks)可以用多少时间.
                    // Ensure we always run tasks.
                    final long ioTime = System.nanoTime() - ioStartTime;
                    ranTasks = runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
                }
            } else {
   
   
                ranTasks = runAllTasks(0); // This will run the minimum number of tasks
            }

            // 渠道任务设置 selectCnt 为 0
            if (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
            } else if (unexpectedSelectorWakeup(selectCnt)) {
   
    // Unexpected wakeup (unusual case)
                selectCnt = 0;
            }
        } catch (CancelledKeyException e) {
   
   
            // Harmless exception - log anyway
            if (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

private boolean unexpectedSelectorWakeup(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/2426
        if (logger.isDebugEnabled()) {
   
   
            logger.debug("Selector.select() returned prematurely because " +
                    "Thread.currentThread().interrupt() was called. Use " +
                    "NioEventLoop.shutdownGracefully() to shutdown the NioEventLoop.");
        }
        return true;
    }
    // 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里面去, 并替换当前的selector
        rebuildSelector();
        return true;
    }
    return false;
}
public void rebuildSelector() {
   
   
    if (!inEventLoop()) {
   
   
        execute(new Runnable() {
   
   
            @Override
            public void run() {
   
   
                // 把老的selectedKeys都注册到一个新的selector里面去, 替换当前的selector
                rebuildSelector0();
            }
        });
        return;
    }
    // 把老的selectedKeys都注册到一个新的selector里面去, 替换当前的selector
    rebuildSelector0();
}
private void rebuildSelector0() {
   
   
    final Selector oldSelector = selector;
    final SelectorTuple 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;
    // 转移 SelectionKey
    for (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 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);
            }
        }
    }

    // 设置 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.");
    }

处理流程

1.server端绑定端口

public ChannelFuture bind(int inetPort) {
   
   
    return bind(new InetSocketAddress(inetPort));
}
private ChannelFuture doBind(final SocketAddress localAddress) {
   
   
    // 初始化 NioServerSocketChannel 或者 NioSocketChannel
    // 组装好 pipeline 中要添加的 ChannelHandler, 并回调对应的事件
    final ChannelFuture regFuture = initAndRegister();
    final Channel 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.
        final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
        regFuture.addListener(new ChannelFutureListener() {
   
   
            @Override
            public void 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值