Netty源码之启动过程

本文章基于netty4.1.25版本

<dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.25.Final</version>
        </dependency>

我们在使用Netty的时候的初始化代码一般如下

EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
    // 配置服务器的NIO线程组
    ServerBootstrap b = new ServerBootstrap();
    b.group(bossGroup, workerGroup)
        .channel(NioServerSocketChannel.class)
        .option(ChannelOption.SO_BACKLOG, 1024)
        .childHandler(new ChildChannelHandler());

    // 绑定端口,同步等待成功
    ChannelFuture f = b.bind(port).sync();
    // 等待服务端监听端口关闭
    f.channel().closeFuture().sync();
} finally {
    // 优雅退出,释放线程池资源
    bossGroup.shutdownGracefully();
    workerGroup.shutdownGracefully();
}

一 NioEventLoopGroup初始化线程池

NioEventLoopGroup从名字上就可以看出来是由多个NioEventLoop组成
在这里插入图片描述

可以看到NioEventLoop继承自SingleThreadEventExecutor,是一个单线程的executor,在线程中死循环监听IO事件,那么NioEventLoopGroup就是多个NioEventLoop组成的线程池

首先来了解NioEventLoopGroup的构造方法,如果使用无参的构造方法的话,最后会执行下面这个构造方法,这里面做要做了以下几件事

  1. 如果executor没有初始化,使用默认的executor初始化
  2. 初始化线程池中每个EventLoop
  3. 如果其中一个初始化过程中抛出异常,关闭所有的NioEventLoop
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                        EventExecutorChooserFactory chooserFactory, Object... args) {
    if (nThreads <= 0) {
        throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
    }

    if (executor == null) {
        executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
    }

    children = new EventExecutor[nThreads];

    for (int i = 0; i < nThreads; i ++) {
        boolean success = false;
        try {
            // 创建EventLoop
            children[i] = newChild(executor, args);
            success = true;
        } catch (Exception e) {
            // TODO: Think about if this is a good exception type
            throw new IllegalStateException("failed to create a child event loop", e);
        } finally {
            if (!success) {
                for (int j = 0; j < i; j ++) {
                    children[j].shutdownGracefully();
                }

                for (int j = 0; j < i; j ++) {
                    EventExecutor e = children[j];
                    try {
                        while (!e.isTerminated()) {
                            e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
                        }
                    } catch (InterruptedException interrupted) {
                        // Let the caller handle the interruption.
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            }
        }
    }

    // 初始化chooser,决定选择下一个线程的策略
    chooser = chooserFactory.newChooser(children);

    final FutureListener<Object> terminationListener = new FutureListener<Object>() {
        @Override
        public void operationComplete(Future<Object> future) throws Exception {
            if (terminatedChildren.incrementAndGet() == children.length) {
                terminationFuture.setSuccess(null);
            }
        }
    };

    for (EventExecutor e: children) {
        e.terminationFuture().addListener(terminationListener);
    }

    Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
    Collections.addAll(childrenSet, children);
    readonlyChildren = Collections.unmodifiableSet(childrenSet);
}

接下来介绍主要参数

nThreads
// 默认的线程池大小
private static final int DEFAULT_EVENT_LOOP_THREADS;

static {
    // 如果配置了io.netty.eventLoopThreads参数的话,先取该参数的值
    // 如果没有配置上面的参数,则取机器处理器个数的2倍
    // 如果上面算出的结果小于1则取1
    DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
        "io.netty.eventLoopThreads", Runtime.getRuntime().availableProcessors() * 2));

    if (logger.isDebugEnabled()) {
        logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS);
    }
}

// 默认没有指定线程池大小,取DEFAULT_EVENT_LOOP_THREADS
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
    super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}
executor

默认没有指定executor,为null,使用默认的executor初始化

if (executor == null) {
            executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
chooserFactory
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
        this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args);
    }

使用默认的chooser,该类的主要功能是提供选择下一个线程的策略

public final class DefaultEventExecutorChooserFactory implements EventExecutorChooserFactory {
	// 单例
    public static final DefaultEventExecutorChooserFactory INSTANCE = new DefaultEventExecutorChooserFactory();

    private DefaultEventExecutorChooserFactory() { }

    @SuppressWarnings("unchecked")
    @Override
    public EventExecutorChooser newChooser(EventExecutor[] executors) {
        if (isPowerOfTwo(executors.length)) {
            // 如果是2的幂次则使用这个chooser
            return new PowerOfTowEventExecutorChooser(executors);
        } else {
            return new GenericEventExecutorChooser(executors);
        }
    }

    private static boolean isPowerOfTwo(int val) {
        // 判断一个数是否2的幂,方法很巧妙
        return (val & -val) == val;
    }

    private static final class PowerOfTowEventExecutorChooser implements EventExecutorChooser {
        private final AtomicInteger idx = new AtomicInteger();
        private final EventExecutor[] executors;

        PowerOfTowEventExecutorChooser(EventExecutor[] executors) {
            this.executors = executors;
        }

        @Override
        public EventExecutor next() {
            // 如果是2的幂次个线程,可以使用位运算计算出下一个选出的线程的index
            return executors[idx.getAndIncrement() & executors.length - 1];
        }
    }

    private static final class GenericEventExecutorChooser implements EventExecutorChooser {
        private final AtomicInteger idx = new AtomicInteger();
        private final EventExecutor[] executors;

        GenericEventExecutorChooser(EventExecutor[] executors) {
            this.executors = executors;
        }

        @Override
        public EventExecutor next() {
            // 使用求余的方法计算出下一个线程的index
            return executors[Math.abs(idx.getAndIncrement() % executors.length)];
        }
    }
}

可以看出上面两个chooser计算出的最终结果是一致的,但是使用位运算更快一点,所以如果是线程池的大小刚好是2的幂次的话使用位运算的chooser。

args
// args[0],下面方法返回的provider,在linux平台上默认是EPollSelectorProvider
java.nio.channels.spi.SelectorProvider#provider
// args[1],决定eventLoop每次执行select还是执行队列中的任务
io.netty.channel.DefaultSelectStrategyFactory
// args[2],等待队列满以后的拒绝策略
io.netty.util.concurrent.RejectedExecutionHandlers#REJECT

初始化NioEventLoopGroup过程主要是为了初始化线程池中每一个NioEventLoop,而每一个NioEventLoop包含一个selector。

参数了解了,就可以看初始化selector的逻辑了

二 初始化selector

上面说到的初始化NioEventLoop,调用newChild方法来初始化

children[i] = newChild(executor, args);

具体逻辑

protected EventLoop newChild(Executor executor, Object... args) throws Exception {
    // 下面这几个参数上面已经介绍过
    return new NioEventLoop(this, executor, (SelectorProvider) args[0],
                            ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
}

NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
             SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
    // 调用父类构造方法初始化taskQueue,taskQueue的大小取Math.max(16, maxPendingTasks)
    super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
    // 校验selectorProvider
    if (selectorProvider == null) {
        throw new NullPointerException("selectorProvider");
    }
    // 校验EventLoop每次执行的select策略是否为空
    if (strategy == null) {
        throw new NullPointerException("selectStrategy");
    }
    provider = selectorProvider;
    // 初始化selector
    selector = openSelector();
    selectStrategy = strategy;
}

再看openSelector方法

private Selector openSelector() {
    final Selector selector;
    try {
        // 调用的是sun.nio.ch.EPollSelectorProvider#openSelector
        // 返回的是sun.nio.ch.EPollSelectorImpl
        selector = provider.openSelector();
    } catch (IOException e) {
        throw new ChannelException("failed to open a new selector", e);
    }

    // 是否使用SelectedSelectionKeySet优化,默认不禁用false
    if (DISABLE_KEYSET_OPTIMIZATION) {
        return selector;
    }

    // Netty优化过后的
    final SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();

    // 尝试获取SelectorImpl对象,后续会使用反射操作这个类的属性
    Object maybeSelectorImplClass = AccessController.doPrivileged(new PrivilegedAction<Object>() {
        @Override
        public Object run() {
            try {
                return Class.forName(
                    "sun.nio.ch.SelectorImpl",
                    false,
                    PlatformDependent.getSystemClassLoader());
            } catch (ClassNotFoundException e) {
                return e;
            } catch (SecurityException e) {
                return e;
            }
        }
    });

    // 确保有权限访问该类
    if (!(maybeSelectorImplClass instanceof Class) ||
        // ensure the current selector implementation is what we can instrument.
        !((Class<?>) maybeSelectorImplClass).isAssignableFrom(selector.getClass())) {
        if (maybeSelectorImplClass instanceof Exception) {
            Exception e = (Exception) maybeSelectorImplClass;
            logger.trace("failed to instrument a special java.util.Set into: {}", selector, e);
        }
        return selector;
    }

    final Class<?> selectorImplClass = (Class<?>) maybeSelectorImplClass;

    Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
        @Override
        public Object run() {
            try {
                // 得到字段selectedKeys
                Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
                // 得到字段publicSelectedKeys
                Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");

                selectedKeysField.setAccessible(true);
                publicSelectedKeysField.setAccessible(true);

                // 将selectedKeys、publicSelectedKeys均设置为Netty自定义的SelectedSelectionKeySet
                selectedKeysField.set(selector, selectedKeySet);
                publicSelectedKeysField.set(selector, selectedKeySet);
                return null;
            } catch (NoSuchFieldException e) {
                return e;
            } catch (IllegalAccessException e) {
                return e;
            } catch (RuntimeException e) {
                // JDK 9 can throw an inaccessible object exception here; since Netty compiles
                // against JDK 7 and this exception was only added in JDK 9, we have to weakly
                // check the type
                if ("java.lang.reflect.InaccessibleObjectException".equals(e.getClass().getName())) {
                    return e;
                } else {
                    throw e;
                }
            }
        }
    });

    if (maybeException instanceof Exception) {
        selectedKeys = null;
        Exception e = (Exception) maybeException;
        logger.trace("failed to instrument a special java.util.Set into: {}", selector, e);
    } else {
        selectedKeys = selectedKeySet;
        logger.trace("instrumented a special java.util.Set into: {}", selector);
    }

    return selector;
}

初始化selector的过程中主要做了几件事:

使用平台相关的provider初始化对应的SelectorImpl(window或者linux),这里使用了Java的SPI来加载平台相关的provider,每一种provider又对应一种SelectorImpl
如果没有禁用selectedKey优化,Netty会使用自定的SelectedSelectionKeySet替换SelectorImpl的publicSelectedKeys、selectedKeys

对SelectorImpl.selectedKey优化的说明:
利用反射将SelectorImpl.selectedKey替换成了SelectedSelectionKeySet,SelectedSelectionKeySet利用数组实现元素存放
在调用select方法的时候如果有事件进来的时候会调用SelectedSelectionKeySet#add,将有IO事件的selectKey添加到keyset中
使用数组遍历(processSelectedKeysOptimized)要比使用set遍历快一些。在Java9以后这个优化就失效了,因为Java9引入了Jigsaw

接下来看看Selector创建过程,上面调用了EPollSelectorProvider#openSelector来开始初始化selector

ublic AbstractSelector openSelector() throws IOException {
    // 直接new 一个EPollSelectorImpl
    return new EPollSelectorImpl(this);
}

// 该构造方法只能是包内使用,供provider来调用
EPollSelectorImpl(SelectorProvider sp) throws IOException {
    // 调用父类SelectorImpl的构造方法初始化selectedKeys、publicKeys、publicSelectedKeys
    // 上面已经说过了,如果使用Netty的优化,publicKeys、publicSelectedKey会被替换
    super(sp);
    // 调用linux的pipe方法,创建一个管道,配置为非阻塞的
    long pipeFds = IOUtil.makePipe(false);
    // 高32为读文件描述符
    fd0 = (int) (pipeFds >>> 32);
    // 低32位为写文件描述符
    fd1 = (int) pipeFds;
    // EPollArrayWrapper包含一系列native方法来调用EPollArrayWrapper.c本地方法
    pollWrapper = new EPollArrayWrapper();
    pollWrapper.initInterrupt(fd0, fd1);
    // fdToKey用来保存文件描述符和SelectionKeyImpl的映射
    fdToKey = new HashMap<>();
}

EPollArrayWrapper() throws IOException {
    // creates the epoll file descriptor
    // 创建epoll的文件描述符
    epfd = epollCreate();

    // the epoll_event array passed to epoll_wait
    int allocationSize = NUM_EPOLLEVENTS * SIZE_EPOLLEVENT;
    pollArray = new AllocatedNativeObject(allocationSize, true);
    pollArrayAddress = pollArray.address();

    // eventHigh needed when using file descriptors > 64k
    if (OPEN_MAX > MAX_UPDATE_ARRAY_SIZE)
        eventsHigh = new HashMap<>();
}

来到了创建epoll文件描述符相关代码了,上面这个还是看不到究竟调用了哪些本地方法,我们看看相关的c代码

// jdk/src/solaris/native/sun/nio/ch/IOUtil.c
JNIEXPORT jlong JNICALL
Java_sun_nio_ch_IOUtil_makePipe(JNIEnv *env, jobject this, jboolean blocking)
{
    int fd[2];

    // 打开pipe
    if (pipe(fd) < 0) {
        JNU_ThrowIOExceptionWithLastError(env, "Pipe failed");
        return 0;
    }
    if (blocking == JNI_FALSE) {
        // 配置管道为非阻塞
        if ((configureBlocking(fd[0], JNI_FALSE) < 0)
            || (configureBlocking(fd[1], JNI_FALSE) < 0)) {
            JNU_ThrowIOExceptionWithLastError(env, "Configure blocking failed");
            close(fd[0]);
            close(fd[1]);
            return 0;
        }
    }
    // 将读写文件描述符放入一个long型中返回
    return ((jlong) fd[0] << 32) | (jlong) fd[1];
}

// jdk/src/solaris/native/sun/nio/ch/EPollArrayWrapper.c
JNIEXPORT jint JNICALL
Java_sun_nio_ch_EPollArrayWrapper_epollCreate(JNIEnv *env, jobject this)
{
    /*
     * epoll_create expects a size as a hint to the kernel about how to
     * dimension internal structures. We can't predict the size in advance.
     */
    // 这里调用linux函数epoll_create创建epoll的文件描述符
    int epfd = epoll_create(256);
    if (epfd < 0) {
        JNU_ThrowIOExceptionWithLastError(env, "epoll_create failed");
    }
    return epfd;
}

总结:newChild()方法调用NioEventLoop的构造方法创建新的NioEventLoop,构造方法里调用openSelector方法初始为每一个NioEventLoop创建一个selector。

接下来就是将需要监听的描述符注册到epoll上,对应到Netty就是将channel注册到selector上

三 bind方法 初始化channel

 ChannelFuture future = bootstrap.bind(port).sync();

最终调用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;
        }
    }

分析initAndRegister方法

// 配置使用的channel的时候会指定对应的channelFactory
final ChannelFuture initAndRegister() {
    Channel channel = null;
    try {
        // channelFactory是NioServerSocketChannel
        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);
        }
        // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
        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();
        }
    }

    // If we are here and the promise is not failed, it's one of the following cases:
    // 1) If we attempted registration from the event loop, the registration has been completed at this point.
    //    i.e. It's safe to attempt bind() or connect() now because the channel has been registered.
    // 2) If we attempted registration from the other thread, the registration request has been successfully
    //    added to the event loop's task queue for later execution.
    //    i.e. It's safe to attempt bind() or connect() now:
    //         because bind() or connect() will be executed *after* the scheduled registration task is executed
    //         because register(), bind(), and connect() are all bound to the same thread.

    return regFuture;
}

利用反射创建实例,使用的是NioServerSocketChannel的无参构造方法,在无参造方法中调用newChannel,查看NioServerSocketChannel的无参构造方法

 private static ServerSocketChannel newSocket(SelectorProvider provider) {
        try {
            /**
             *  Use the {@link SelectorProvider} to open {@link SocketChannel} and so remove condition in
             *  {@link SelectorProvider#provider()} which is called by each ServerSocketChannel.open() otherwise.
             *
             *  See <a href="https://github.com/netty/netty/issues/2308">#2308</a>.
             */
            return provider.openServerSocketChannel();
        } catch (IOException e) {
            throw new ChannelException(
                    "Failed to open a server socket.", e);
        }
    }

public ServerSocketChannel openServerSocketChannel() throws IOException {
        return new ServerSocketChannelImpl(this);
}
ServerSocketChannelImpl(SelectorProvider var1) throws IOException {
        super(var1);
         // 创建一个socket,返回的是socket对应的文件描述符
        this.fd = Net.serverSocket(true);
        this.fdVal = IOUtil.fdVal(this.fd);
        this.state = 0;
    }
// sun.nio.ch.Net#serverSocket
static FileDescriptor serverSocket(boolean stream) {
    // socket0是一个native方法,返回的是int类型的linux的文件描述符,使用newFD转化为Java的文件描述符
    return IOUtil.newFD(socket0(isIPv6Available(), stream, true));
}

// jdk/src/solaris/native/sun/nio/ch/Net.c
JNIEXPORT int JNICALL
Java_sun_nio_ch_Net_socket0(JNIEnv *env, jclass cl, jboolean preferIPv6,
                            jboolean stream, jboolean reuse)
{
	// 省略中间代码...
    // 调用socket方法创建一个socket,并返回对应的文件描述符
    fd = socket(domain, type, 0);
    if (fd < 0) {
        return handleSocketError(env, errno);
    }

	// 省略中间代码...
    return fd;
}

在静态代码块中完成初始化,调用openServerSocketChannel()方法返回一个ServerSocketChannelImpl,主要逻辑就是在返回的channel中创建了一个socket,接下来看看channel的注册

四 bind方法 channel的注册

继续bind的后续方法


ChannelFuture regFuture = config().group().register(channel);
  1. config()返回的是ServerBootstrapConfig
  2. group()返回的是parentGroup,对应开始的例子是bossGroup,也就是NioEventLoopGroup
  3. 所以是调用的是NioEventLoopGroup.register,该方法继承自MultithreadEventLoopGroup

查看register方法

// io.netty.channel.MultithreadEventLoopGroup#register(io.netty.channel.Channel)
public ChannelFuture register(Channel channel) {
    // 使用的是bossGroup,next方法选出第一个NioEventLoop,调用NioEventLoop.register,该方法继承自SingleThreadEventLoop
    return next().register(channel);
}

// io.netty.channel.SingleThreadEventLoop#register(io.netty.channel.Channel)
public ChannelFuture register(Channel channel) {
    // 注册的还是使用一个promise,可以异步注册
    return register(new DefaultChannelPromise(channel, this));
}

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

debug跟进:channel返回的是NioServerSocketChannel,unsafe返回io.netty.channel.nio.AbstractNioMessageChannel.NioMessageUnsafe, 所以调用的是NioMessageUnsafe.register,该方法继承自AbstractUnsafe

// io.netty.channel.AbstractChannel.AbstractUnsafe#register
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
    // 省略中间检查代码...
	// 当前线程是main线程,eventLoop是bossGroup中的一个线程,所以这里返回false,会在新线程中执行register0
    if (eventLoop.inEventLoop()) {
        register0(promise);
    } else {
        try {
            // 在eventLoop中执行
            eventLoop.execute(new Runnable() {
                @Override
                public void run() {
                    register0(promise);
                }
            });
        } catch (Throwable t) {
		// 省略中间代码...
        }
    }
}

查看register0方法

private void register0(ChannelPromise promise) {
    try {
		// 省略中间检查代码...
        // 这里面主要是调用ServerSocketChannelImpl.register,注册的过程中主要是将需要监听的文件描述符添加到EPollArrayWrapper中
        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) {
        // 省略中间代码...
    }
}

下面看看channel注册过程中doRegister()做了哪些事情

protected void doRegister() throws Exception {
        boolean selected = false;
        for (;;) {
            try {
                selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
                return;
            } catch (CancelledKeyException e) {
                if (!selected) {
                    // Force the Selector to select now as the "canceled" SelectionKey may still be
                    // cached and not removed because no Select.select(..) operation was called yet.
                    eventLoop().selectNow();
                    selected = true;
                } else {
                    // We forced a select operation on the selector before but the SelectionKey is still cached
                    // for whatever reason. JDK bug ?
                    throw e;
                }
            }
        }
    }

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

public final SelectionKey register(Selector sel, int ops,
                                       Object att)
        throws ClosedChannelException
    {
        synchronized (regLock) {
            if (!isOpen())
                throw new ClosedChannelException();
            if ((ops & ~validOps()) != 0)
                throw new IllegalArgumentException();
            if (blocking)
                throw new IllegalBlockingModeException();
            SelectionKey k = findKey(sel);
            if (k != null) {
                k.interestOps(ops);
                k.attach(att);
            }
            if (k == null) {
                // New registration
                synchronized (keyLock) {
                    if (!isOpen())
                        throw new ClosedChannelException();
                    k = ((AbstractSelector)sel).register(this, ops, att);
                    addKey(k);
                }
            }
            return k;
        }
    }

前面都是一些检查逻辑,这里主要查看k = ((AbstractSelector)sel).register(this, ops, att);

// sun.nio.ch.SelectorImpl#register
// 这里ch是ServerSocketChannelImpl
// attachment是NioServerSocketChannel
// ops是0,这里并不注册需要监听的事件
// selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
protected final SelectionKey register(AbstractSelectableChannel ch,
                                      int ops,
                                      Object attachment)
{
    if (!(ch instanceof SelChImpl))
        throw new IllegalSelectorException();
    // 创建一个SelectionKeyImpl,
    SelectionKeyImpl k = new SelectionKeyImpl((SelChImpl)ch, this);
    k.attach(attachment);
    synchronized (publicKeys) {
        // 调用sun.nio.ch.EPollSelectorImpl#implRegister
        implRegister(k);
    }
    // 设置当前channel关注的事件
    k.interestOps(ops);
    return k;
}

protected void implRegister(SelectionKeyImpl ski) {
    if (closed)
        throw new ClosedSelectorException();
    SelChImpl ch = ski.channel;
    int fd = Integer.valueOf(ch.getFDVal());
    fdToKey.put(fd, ski);
    // poolWrapper是epoll监听事件所需数据结构的java版本
    // add方法调用setUpdateEvents来指定当前socket监听的事件
    pollWrapper.add(fd);
    keys.add(ski);
}

/**
 * struct epoll_event {
 *     __uint32_t events;
 *     epoll_data_t data;
 * };
 * 由于一开始并不知道会监听多少个socket,所以jdk默认指定了MAX_UPDATE_ARRAY_SIZE
 * 如果小于MAX_UPDATE_ARRAY_SIZE则使用数组eventsLow存储每个socket监听的事件,eventsLow的下标就是socket对应的文件描述符
 * 如果大于等于MAX_UPDATE_ARRAY_SIZE个则使用EPollArrayWrapper#eventsHigh,也就是一个map来保存每个socket监听的事件
 * 
 * 注意这个时候调用setUpdateEvents的events参数是0,也就是还没有执行监听的事件类型
 */
private void setUpdateEvents(int fd, byte events, boolean force) {
    if (fd < MAX_UPDATE_ARRAY_SIZE) {
        if ((eventsLow[fd] != KILLED) || force) {
            eventsLow[fd] = events;
        }
    } else {
        Integer key = Integer.valueOf(fd);
        if (!isEventsHighKilled(key) || force) {
            eventsHigh.put(key, Byte.valueOf(events));
        }
    }
}

需要注意的时候上面并没有设置当前channel监听的事件,真正设置监听的事件类型是在beginRead方法里面,在当前channel被激活的时候会调用beginRead方法

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();
                    }
                }

beginRead方法

protected void doBeginRead() throws Exception {
    // Channel.read() or ChannelHandlerContext.read() was called
    final SelectionKey selectionKey = this.selectionKey;
    if (!selectionKey.isValid()) {
        return;
    }

    readPending = true;

    final int interestOps = selectionKey.interestOps();
    if ((interestOps & readInterestOp) == 0) {
        // readInterestOp是16,在NioServerSocketChannel构造方法里面指定了这个channel需要监听accept事件
        // 这里才是真正设置socket监听事件的地方
        // 下面这个方法最后会调用到sun.nio.ch.EPollArrayWrapper#setInterest
        selectionKey.interestOps(interestOps | readInterestOp);
    }
}

// sun.nio.ch.EPollArrayWrapper#setInterest
void setInterest(int fd, int mask) {
    synchronized (updateLock) {
        // record the file descriptor and events
        int oldCapacity = updateDescriptors.length;
        if (updateCount == oldCapacity) {
            int newCapacity = oldCapacity + INITIAL_PENDING_UPDATE_SIZE;
            int[] newDescriptors = new int[newCapacity];
            System.arraycopy(updateDescriptors, 0, newDescriptors, 0, oldCapacity);
            updateDescriptors = newDescriptors;
        }
        updateDescriptors[updateCount++] = fd;

        // events are stored as bytes for efficiency reasons
        byte b = (byte)mask;
        assert (b == mask) && (b != KILLED);
        // 上面已经说过这个方法了,把当前socket对应的文件描述符监听的事件设置为b
        setUpdateEvents(fd, b, false);
    }
}

到这里一个serverSocketChannel注册成功了,而且也设置了关注的事件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值