Netty框架Bind流程浅析

Netty简介:

Netty是JBOSS 基于Java NIO开源的提供异步、事件驱动的网络通信框架,用于开发高性能网络服务器
Zookeeper RocketMq Dubbo ShardingSphere 底层通信都有用到它

服务端代码实例

public class DiscardServer {
    private int port;

    public DiscardServer(int port) {
        this.port = port;
    }

    public void run() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap(); // (2)
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class) // (3)
                    .childHandler(new ChannelInitializer<SocketChannel>() { // (4)
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new DiscardServerHandler());
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128)          // (5)
                    .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)

            // Bind and start to accept incoming connections.
            ChannelFuture f = b.bind(port).sync(); // (7)

            // Wait until the server socket is closed.
            // In this example, this does not happen, but you can do that to gracefully
            // shut down your server.
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        int port = 8080;
        if (args.length > 0) {
            port = Integer.parseInt(args[0]);
        }

        new DiscardServer(port).run();
    }
}
public class DiscardServerHandler extends ChannelInboundHandlerAdapter { // (1)

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)
        // Discard the received data silently.
        ((ByteBuf) msg).release(); // (3)
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
        // Close the connection when an exception is raised.
        cause.printStackTrace();
        ctx.close();
    }
}

这个是netty官网上的代码,服务端的netty代码基本都是这个模板,在执行bind操作后就可以监听端口接收客户端请求了
这个模板代码里面主要有以下几个步骤

  1. 创建服务端启动引导类 ServerBootstrap
  2. ServerBootstrap 绑定两个EventLoopGroup ,第一个处理网络连接,另外一个处理业务请求(约定处理连接的叫bossGroup ,处理业务的叫workerGroup )
  3. 绑定Channel
  4. 设置workerGroup 的业务逻辑处理Handler
  5. 设置网络相关参数
  6. 执行bind操作,开始监听端口接收客户端请求

netty组件

ServerBootstrap:

服务端引导程序启动类,继承了AbstractBootstrap
在这里插入图片描述
内部属性

属性描述
Map<ChannelOption<?>, Object> childOptionswork线程channel相关参数
EventLoopGroup childGroup实际处理业务的工作者 事件循环线程池
ChannelHandler childHandler处理添加到工作者事件循环线程的Channel

AbstractBootstrap:

内部属性

属性描述
EventLoopGroup group事件循环线程池负责接收外部请求,一般内部只要一个NioEventLoop就可以
ChannelFactory<? extends C> channelFactory实际处理业务的工作者 事件循环线程池
SocketAddress localAddressSocket地址可通过构造函数或者localAddress方法赋值,这个属性有值可以调用bind方法不用传端口信息
ChannelFactory<? extends C> channelFactory实现类是ReflectiveChannelFactory ,负责创建Channel NioServerSocketChannel 或者NioSocketChannel ,channel方法传入具体Channel类构建
Map<ChannelOption<?>, Object>定义了一些网络有关的参数
ChannelHandler handlerBossGroup绑定的ChannelHandler处理类

Bootstrap

客户端引导类同样父类继承了AbstractBootstrap,只需要一个事件循环线程池

内部属性

属性描述
AddressResolverGroup<?>地址解析器
SocketAddress remoteAddress服务端远程地址

NioEventLoopGroup

事件循环线程池 Bootstrap内部处理任务的线程池组

在这里插入图片描述

类继承体系及作用

类或者接口作用
Executor执行器接口 NioEventLoopGroup可以执行execute方法
ExecutorService扩展执行器接口 定义了shutdown submit invokeAll invokeAny等方法
ScheduledExecutorService表示NioEventLoopGroup可执行调度任务
Iterable迭代器接口
EventExecutorGroup定义了next方法 获取Group内的下一个事件循环线程
AbstractEventExecutorGroup实现了一些执行submit invokeAll schedule 执行任务的方法 ,next()方法获取下一个事件循环线程实际执行
EventLoopGroup定义next方法获取Group里面的线程 另外提供了三个register重载方法 将Channel具体绑定到一个 EventLoop
MultithreadEventExecutorGroup有集合记录了具体的EventLoop 实现了next方法的功能
MultithreadEventLoopGroup实现了Channel注册功能
NioEventLoopGroupnewChild 方法创建一个NioEventLoop

NioEventLoop

在这里插入图片描述

类继承体系及作用

类或者接口作用
EventExecutorGroup接口 : 提供了获取下一个EventExecutor的方法 EventExecutor迭代器的方法
EventExecutor接口 起到标记作用 定义了inEventLoop方法
AbstractEventExecutor实现了submit提交任务方法 交由父类AbstractExecutorService 执行
AbstractExecutorServiceJDK并发包里面的类 实现了 submit invokeAny 提交,执行任务的方法, 包装Runnable Callable 接口成FutureTask 的newTaskFo方法
AbstractScheduledEventExecutor实现了ScheduledExecutorService的方法 内部有一个ScheduledFutureTask 的优先级队列 提供一些获取 ScheduledFutureTask 的方法给子类调用
SingleThreadEventExecutor提供抽象方法run 调用NioEventLoop Run方法 ,实现了线程池的大部分方法
SingleThreadEventLoopregister 方法将Channel绑定到一个事件循环线程
NioEventLoop有Nio 的Selector选择器类,run方法筛选注册在Selector上的Channel

NioServerSocketChannel

服务端Channel
在这里插入图片描述

类继承体系及作用

类或接口作用
AbstractChannel比较顶层的实现,这个类定义了DefaultChannelPipeline Unsafe对象 没有涉及到Java NIO方面的概念
AbstractNioChannel这里开始关联到jJava NIO方面的概念 有提供方法往事件循环线程池对应Selector注册SelectionKey
AbstractNioMessageChannel实现了读方法,接收到客户端请求后交给workGroup执行

NioSocketChannel

在这里插入图片描述

DefaultChannelPipeline

在这里插入图片描述

AbstractChannel (NioSocketChannel NioServerSocketChannel 都继承了这个类) 类有一个 DefaultChannelPipeline 属性
DefaultChannelPipeline 有两个 AbstractChannelHandlerContext属性,分别是hean tail,看命名就知道 AbstractChannelHandlerContext的数据能组成一个链表结构(也就是Netty中的Channel内部有一个链表来处理请求)

HeadContext

在这里插入图片描述

TailContext

在这里插入图片描述

可以看到 HeadContext 比TailContext多继承了一个ChannelOutboundHandler接口(提供write flush方法),所以向网络的另外一端写出数据最终是通过HeadContext

服务端bind过程

bind之后可以监听端口,我们来看看具体过程,代码入口在AbstractBootstrap doBind方法

 private ChannelFuture doBind(final SocketAddress localAddress) {
        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 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;
        }
    }

第2-3行通过initAndRegister方法创建一个Channel (这里的Channel 模板方法channel传进来的对象,服务端启动传的是NioServerSocketChannel)跟ChannelFuture ,ChannelFuture 实现了JDK里面的Future接口,这个是在后面Channel注册到事件循环线程池结束后来回调的

我们接着看doBind 后面的代码第4到6 ,7到12行代码分别判断这个异步Future是否异常 或者完成

在netty服务端启动流程中,正常情况下都是走到13行的else分支代码的,这里创建了一个PendingRegistrationPromise对象,并在前面返回的Future对象上添加一个监听,这个监听在什么时候执行呢,就是Channel注册到事件循环线程池的逻辑完成后(无论成功注册或者异常都会回调),当然注册成功之后会执行doBind0方法

private static void doBind0(
            final ChannelFuture regFuture, final Channel channel,
            final SocketAddress localAddress, final ChannelPromise promise) {

        // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up
        // the pipeline in its channelRegistered() implementation.
        channel.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                if (regFuture.isSuccess()) {
                    channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
                } else {
                    promise.setFailure(regFuture.cause());
                }
            }
        });
    }

这个doBind0会提交一个任务到Channel绑定的事件循环线程池等待执行(如果线程池没启动的话会先启动),然后我们具体看看这个后面要被线程池执行的任务具体做了什么,调用Channel的bind方法,这里的Channel 还是Netty层面的channel,bind操作现在Channel DefaultChannelPipeline上传递,从TailContext节点开始,HeadContext 通过Unsafe bind方法最终调用到Channel的doBind方法
NioServerSocketChannel里面这个方法的实现是

// NioServerSocketChannel
 protected void doBind(SocketAddress localAddress) throws Exception {
        if (PlatformDependent.javaVersion() >= 7) {
            javaChannel().bind(localAddress, config.getBacklog());
        } else {
            javaChannel().socket().bind(localAddress, config.getBacklog());
        }
}
// AbstractNioChannel
protected SelectableChannel javaChannel() {
        return ch;
}

代码执行到这就可以监听端口接收客户端请求
前面说到,bind方法是在Channel注册完成后才会调用,我们再回头看看Channel怎么注册的

服务端Channel注册流程

创建跟注册Channel的代码是AbstractBootstrap initAndRegister

  final ChannelFuture initAndRegister() {
        Channel channel = null;
        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);
            }    
            return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
        }   
        ChannelFuture regFuture = config().group().register(channel);
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }
       return regFuture;
    }
  1. 第4行创建了一个Channel
  2. 第5行子类ServerBootstrap实现,在Channel的DefaultChannelPromise处理链表上增加了一个Handler节点
  p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(final Channel ch) {
                final ChannelPipeline pipeline = ch.pipeline();
                ChannelHandler handler = config.handler();
                if (handler != null) {
                    pipeline.addLast(handler);
                }
                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });
public void channelRead(ChannelHandlerContext ctx, Object msg) {
            final Channel child = (Channel) msg;

            child.pipeline().addLast(childHandler);

            setChannelOptions(child, childOptions, logger);
            setAttributes(child, childAttrs);

            try {
                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);
            }
        }

ServerBootstrapAcceptor 处理读事件的方法channelRead,将接收到的消息(这个消息可以转成Channel 实际是accept接收客户端请求后包装的Channel)注册到childGroup,也就实现了将接收到的请求提交到workGroup执行,具体在哪里传递消息过来的,后面会提到

  1. 将channel注册到BossGroup
 ChannelFuture regFuture = config().group().register(channel);

是的,这里的group就是BossGroup,专门负责处理网络请求的

  // SingleThreadEventLoop
  public ChannelFuture register(final ChannelPromise promise) {
        ObjectUtil.checkNotNull(promise, "promise");
        promise.channel().unsafe().register(this, promise);
        return promise;
    }

netty 里面的这些 register bind write最后都是通过Channel内部的Unsafe来处理

  // AbstractChannel 的 AbstractUnsafe内部类 
  public final void register(EventLoop eventLoop, final ChannelPromise promise) {
            ObjectUtil.checkNotNull(eventLoop, "eventLoop");
            if (isRegistered()) {
                promise.setFailure(new IllegalStateException("registered to an event loop already"));
                return;
            }
            if (!isCompatible(eventLoop)) {
                promise.setFailure(
                        new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
                return;
            }

            AbstractChannel.this.eventLoop = eventLoop;

            if (eventLoop.inEventLoop()) {
                register0(promise);
            } else {
                try {
                    eventLoop.execute(new Runnable() {
                        @Override
                        public void run() {
                            register0(promise);
                        }
                    });
                } catch (Throwable t) {
                    logger.warn(
                            "Force-closing a channel whose registration task was not accepted by an event loop: {}",
                            AbstractChannel.this, t);
                    closeForcibly();
                    closeFuture.setClosed();
                    safeSetFailure(promise, t);
                }
            }
        }

注册方法掉到了AbstractCannel register方法,
第15至16行如果当前线程是事件循环线程里面的线程,直接调用register0方法,这里执行到这里的时候是main线程,所以走的是else分支我们把register0包装成一个任务取异步执行,
这里的register0 调用了doRegister方法,实现了在Selector中注册一个Key的功能

    // AbstractNioChannel
    selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
   // SingleThreadEventExecutor
   private void execute(Runnable task, boolean immediate) {
        boolean inEventLoop = inEventLoop();
        addTask(task);
        if (!inEventLoop) {
            startThread();
            if (isShutdown()) {
                boolean reject = false;
                try {
                    if (removeTask(task)) {
                        reject = true;
                    }
                } catch (UnsupportedOperationException e) {
                    // The task queue does not support removal so the best thing we can do is to just move on and
                    // hope we will be able to pick-up the task before its completely terminated.
                    // In worst case we will log on termination.
                }
                if (reject) {
                    reject();
                }
            }
        }

        if (!addTaskWakesUp && immediate) {
            wakeup(inEventLoop);
        }
    }
  // SingleThreadEventExecutor
 private void startThread() {
        if (state == ST_NOT_STARTED) {
            if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
                boolean success = false;
                try {
                    doStartThread();
                    success = true;
                } finally {
                    if (!success) {
                        STATE_UPDATER.compareAndSet(this, ST_STARTED, ST_NOT_STARTED);
                    }
                }
            }
        }
    }

如果NioEventLoop 没启动调用doStartThread方法

在startThread 方法里面提交了个异步任务调用到了NioEventloop的run方法
总结下Channel注册到事件循环线程池的流程
在这里插入图片描述

NioEventloop run方法

   protected void run() {
        int selectCnt = 0;
        for (;;) {
            try {
                int strategy;
                try {
                    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;
                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) {
                    final long ioStartTime = System.nanoTime();
                    try {
                        processSelectedKeys();
                    } finally {
                        // 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
                }

                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;
                } 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 (Error) 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 (Error) e;
                } catch (Throwable t) {
                    handleLoopException(t);
                }
            }
        }
    }

第7行代码 调用到selector.selectNow() 返回是0,这个方法非阻塞往下执行到54行,调用runAllTasks 执行提交到线程池队列中的任务,前面ServerSocketChannel注册到NioEventLoop的流程中就提交了一个register0任务

strategy = selectStrategy.calculateStrategy(selectNowSupplier, hasTasks());

在这里插入图片描述
前面的runAllTasks方法就会执行到register0
doRegister 向事件循环线程池的Selector注册了一个Key(AbstractNioChannel实现)

private void register0(ChannelPromise promise) {
            try {
                // check if the channel is still open as it could be closed in the mean time when the register
                // call was outside of the eventLoop
                if (!promise.setUncancellable() || !ensureOpen(promise)) {
                    return;
                }
                boolean firstRegistration = neverRegistered;
                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.
                pipeline.invokeHandlerAddedIfNeeded();

                safeSetSuccess(promise);
                pipeline.fireChannelRegistered();
                // Only fire a channelActive if the channel has never been registered. This prevents firing
                // multiple channel actives if the channel is deregistered and re-registered.
                if (isActive()) {
                    if (firstRegistration) {
                        pipeline.fireChannelActive();
                    } else if (config().isAutoRead()) {
                        // This channel was registered before and autoRead() is set. This means we need to begin read
                        // again so that we process inbound data.
                        //
                        // See https://github.com/netty/netty/issues/4805
                        beginRead();
                    }
                }
            } catch (Throwable t) {
                // Close the channel directly to avoid FD leak.
                closeForcibly();
                closeFuture.setClosed();
                safeSetFailure(promise, t);
            }
        }

register0执行完后再次进入NioEventLoop的run方法,这时候第7行代码会返回-1,代码就执行到23行,这里面会调用到selector.select(没有添加调度任务)这个方法会阻塞,客户端有请求进来后会解除阻塞,
客户端有请求进来后解除阻塞,代码往下执行到processSelectedKeys

  private void processSelectedKeys() {
        if (selectedKeys != null) {
            processSelectedKeysOptimized();
        } else {
            processSelectedKeysPlain(selector.selectedKeys());
        }
    }

对于这个selectedKeys openSelector又给它赋值,所有我们直接关注下processSelectedKeysOptimized方法

 private void processSelectedKeysOptimized() {
        for (int i = 0; i < selectedKeys.size; ++i) {
            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;

            final Object a = k.attachment();

            if (a instanceof AbstractNioChannel) {
                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;
            }
        }
    }

前面提到在AbstractNioChannel doRegister方法里面往NioEventLoop的Selector注册了一个selectionKey,第三个参数就是attachment,这里是NioServerSocketChannel

selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);

我们接下来看看processSelectedKey方法

    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
            // &都为1时为1 !=0 说明当前是OP_READ 或者OP_ACCEPT NioServerSocket注册的是0
            // to a spin loop
            if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
                unsafe.read();
            }
        } catch (CancelledKeyException ignored) {
            unsafe.close(unsafe.voidPromise());
        }
    }

NioMessageUnsafe 第25行拿到的值就是AbstractNioChannel doRegister方法里面注册selectionKey传的第二个参数,也就是0,进入到第48行代码 unsafe.read();
第41行在当前Channel(NioServerSocketChannel)的ChannelPipeline处理链表上传递读事件,参数是NioSocketChannel(这个是Netty的Channel 内部有一个java nio的SocketChannel),这个读事件传递下去后续的逻辑是怎么处理的呢,这也就是前面说的在ServerSocketChannel初始化过程中在它的处理链表中加了一个ServerBootstrapAcceptor ,把客户端SocketChannel包装后的NioSocketChannel注册到了workGroup当中,绑定了具体的业务逻辑处理Handler,这样Netty就实现了bossGroup负责接入请求,workGroup负责处理请求的逻辑

在AbstractNioMessageChannel(NioServerSocketChannel)实现了read

    private final class NioMessageUnsafe extends AbstractNioUnsafe {

        private final List<Object> readBuf = new ArrayList<Object>();

        @Override
        public void read() {
            assert eventLoop().inEventLoop();
            final ChannelConfig config = config();
            final ChannelPipeline pipeline = pipeline();
            // ServerSocketChannel创建时 创建了一个ServerSocketChannelConfig 传入了一个AdaptiveRecvByteBufAllocator对象,unsafe().recvBufAllocHandle()拿到的就是这个Allocator的一个内部类HandleImpl
            final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
            allocHandle.reset(config);

            boolean closed = false;
            Throwable exception = null;
            try {
                try {
                    do {
                        // 这里ServerSocketChannel重写方法,accept等待客户端请求接入拿到一个SocketChannel客户端对象
                        int localRead = doReadMessages(readBuf);
                        if (localRead == 0) {
                            break;
                        }
                        if (localRead < 0) {
                            closed = true;
                            break;
                        }

                        allocHandle.incMessagesRead(localRead);
                    } while (allocHandle.continueReading());
                } catch (Throwable t) {
                    exception = t;
                }

                int size = readBuf.size();
                for (int i = 0; i < size; i ++) {
                    readPending = false;
                    /**
                     * readBuf 包装了Nio 的NioSocketChannel
                     */
                    pipeline.fireChannelRead(readBuf.get(i));
                }
                readBuf.clear();
                allocHandle.readComplete();
                pipeline.fireChannelReadComplete();

                if (exception != null) {
                    closed = closeOnReadError(exception);

                    pipeline.fireExceptionCaught(exception);
                }

                if (closed) {
                    inputShutdown = true;
                    if (isOpen()) {
                        close(voidPromise());
                    }
                }
            } 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();
                }
            }
        }
    }
protected int doReadMessages(List<Object> buf) throws Exception {
        /**
         * 对于非阻塞(设置为false) 没有请求进来 accept也会往下走
         */
        SocketChannel ch = SocketUtils.accept(javaChannel());

        try {
            if (ch != null) {
                buf.add(new NioSocketChannel(this, ch));
                return 1;
            }
        } catch (Throwable t) {
            logger.warn("Failed to create a new channel from an accepted socket.", t);

            try {
                ch.close();
            } catch (Throwable t2) {
                logger.warn("Failed to close a socket.", t2);
            }
        }

        return 0;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值