Netty服务器启动源码分析(以NIO为例)

本文深入分析Netty服务器启动源码,以NIO为例,讲解从初始化ServerBootstrap到处理事件的关键步骤,包括ServerBootstrap的属性设置、绑定端口、事件循环及事件处理等核心逻辑。
摘要由CSDN通过智能技术生成

Netty介绍

Netty 由 Trustin Lee(韩国,Line 公司)2004 年开发的一个网络编程框架,简化了基于JDK的NIO编程,所以对于本篇文章,要求读者要了解JDK的NIO编程。Netty不仅简化了基于JDK的NIO编程,同时还具备高性能、可维护、快速开发的特性,它在JDK NIO的基础上做了极大的扩展,满足了程序员开发中常见的应用层协议,优化了一些JDK NIO本身存在的问题(本文以NIO为例)。

Netty服务器启动源码分析

此图是netty服务器端启动时,涉及到的类的一个简化的类图
由于netty本质还是基于JDK实现的,所以服务端启动主要有4个核心点(我们先围绕这四个点展开):

①初始化ServerSocket。
②将ServerSocket注册到Selector中
③绑定端口号
④监听是否有事件发生

初始化ServerBootstrap对象

我们先从ServerBootstrap实例化开始分析,在创建ServerBootstrap,我们会初始化以下参数

volatile EventLoopGroup group;

private volatile ChannelFactory<? extends C> channelFactory;

private volatile SocketAddress localAddress;

private final Map<ChannelOption<?>, Object> options = new ConcurrentHashMap<ChannelOption<?>, Object>();

private final Map<AttributeKey<?>, Object> attrs = new ConcurrentHashMap<AttributeKey<?>, Object>();

private volatile ChannelHandler handler;

private final Map<ChannelOption<?>, Object> childOptions = new ConcurrentHashMap<ChannelOption<?>, Object>();

private final Map<AttributeKey<?>, Object> childAttrs = new ConcurrentHashMap<AttributeKey<?>, Object>();

private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);

private volatile EventLoopGroup childGroup;

private volatile ChannelHandler childHandler;

上述属性值中,参与服务器启动时,主要有下面这四个属性值(其他参数暂时忽略)

volatile EventLoopGroup group;

private volatile EventLoopGroup childGroup;

private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);

private volatile ChannelFactory<? extends C> channelFactory;

在创建好ServerBootstrap实例对象后,我们会调用.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) 这两个方法为group、childGroup、channelFactory这三个参数赋值,其中.group(bossGroup, workerGroup)方法为groupchildGroup赋值,我们在调用.group(bossGroup, workerGroup)方法时会传入两个NioEventLoopGroup,所以此时groupchildGroup的实际类型时NioEventLoopGroup,而.channel(NioServerSocketChannel.class) 方法则是为channelFactory赋值,追进channel方法内部,可以看到有如下操作

public B channel(Class<? extends C> channelClass) {
    return channelFactory(new ReflectiveChannelFactory<C>(
            ObjectUtil.checkNotNull(channelClass, "channelClass")
    ));
 }

channel方法内部内部首先会调用ReflectiveChannelFactory的有参构造,点进ReflectiveChannelFactory类,如下

private final Constructor<? extends T> constructor;

public ReflectiveChannelFactory(Class<? extends T> clazz) {
     ObjectUtil.checkNotNull(clazz, "clazz");
     try {
         //获取无参构造器
         this.constructor = clazz.getConstructor();
     } catch (NoSuchMethodException e) {
         throw new IllegalArgumentException("Class " + StringUtil.simpleClassName(clazz) +
                 " does not have a public non-arg constructor", e);
     }
 }
根据入参,可以得知,此时的channel方法其目的就是根据传入的NioServerSocketChannel.class,获取NioServerSocketChannel.class的无参构造器,为下文实例化NioServerSocketChannel对象做准备。

参数赋值完毕后,紧接着会执行这样一段代码 ChannelFuture f = b.bind(PORT).sync();,此时要关注的是这个.bind(PORT)方法,这个方法会层层调用,最终走到AbstractBootstrap类的doBind(final SocketAddress localAddress)方法。

private ChannelFuture doBind(final SocketAddress localAddress) {
    final ChannelFuture regFuture = initAndRegister();
    final Channel channel = regFuture.channel();
    if (regFuture.cause() != null) return regFuture;
    //不能肯定register完成,因为register是丢到nio event loop里面执行去了。
    if (regFuture.isDone()) {
        // 注册完成
        ChannelPromise promise = channel.newPromise();
        doBind0(regFuture, channel, localAddress, promise);
        return promise;
    } else {
        // NioServerSocket注册未注册完成
        final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
        //等着register完成来通知再执行bind
        regFuture.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) {
                Throwable cause = future.cause();
                // 判断NioServerSocket注册是否发生异常
                if (cause != null) promise.setFailure(cause);//将异常添加到ChannelPromise种
                else {
                    //开始绑定端口
                    promise.registered();
                    doBind0(regFuture, channel, localAddress, promise);
                }
            }
        });
        return promise;
    }
}

doBind(final SocketAddress localAddress)方法内部逻辑中,只关注两个方法initAndRegister()doBind0(regFuture, channel, localAddress, promise);,其他的暂时忽略,其中doBind0(regFuture, channel, localAddress, promise);的作用是将端口号绑定到channel上(也就是上文中提到的③),而initAndRegister()则负责上文提到的①②④三个点,所以我们首先关注initAndRegister()方法,点进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);
            }
            // 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);
        }
        //开始register
        ChannelFuture regFuture = config().group().register(channel);
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }
        return regFuture;
    }

在这段代码中,只需要关注两行代码

channel = channelFactory.newChannel();
 
ChannelFuture regFuture = config().group().register(channel);

首先看一下channelFactory.newChannel();这行代码,该行代码的作用就是根据前文中初始化的无参构造器,获取到该无参构造器对应的实体类的实例对象,所以此时的channel的实际类型是NioServerSocketChannel。再看一下config().group().register(channel)这行代码,先从config()方法开始分析,该方法返回的是前文中初始化的config对象,有兴趣的可以去看一下,在ServerBootstrap类的第274行,所以config().group()其实就是ServerBootstrapConfig.group(),点进group()方法内部。

public final EventLoopGroup group() {
        return bootstrap.group();
}

根据上文中的new ServerBootstrapConfig(this)可以知道,this就是我们创建的ServerBootstrap对象,那么config().group()其实就是ServerBootstrap.group(),而ServerBootstrap.group()返回的就是前文中被赋值的group属性,其实际类型是NioEventLoopGroup,所以第二行代码的意思就是NioEventLoopGroup.register(channel),找到NioEventLoopGroup类,并未找到register方法,所以需要向上寻找,在它的父类MultithreadEventLoopGroup中,可以找到register方法,来看一下register方法。

@Override
public EventLoop next() {
    return (EventLoop) super.next();
}

@Override
public ChannelFuture register(Channel channel) {
    return next().register(channel);
}

它首先会调用本类的next方法,而本类的next方法又指向父类的next方法,而父类返回的是一个EventExecutor对象。那么现在有一个问题。

EventExecutor对象的实际类型是什么?

我们找到MultithreadEventLoopGroup的父类MultithreadEventExecutorGroup,找到这个类的next方法。

@Override
public EventExecutor next() {
    return chooser.next();
}

暂且不管chooser的类型,接着点进next方法,会发现这个next方法是属于EventExecutorChooser接口的,在这个netty框架中,这个接口只有两个实现类,先看一下它的两个实现类。

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

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

    @Override
    //executors总数必须是2的幂次方(2,4,8...等)才会用,&运算效率更高,同时当idx累加成最大值之后,相比较通用的方式(GenericEventExecutorChooser),更公平
    public EventExecutor next() {
        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() {
        //递增、取模,取正值,不然可能是负数,另外:有个非常小的缺点,当idx累加成最大值后,有短暂的不公平:
        //1,2,3,4,5,6,7,0,7(注意这里不是1,而是7,然而往前的第二个也是7,所以不够公平),6,5
        return executors[Math.abs(idx.getAndIncrement() % executors.length)];
    }
}

从代码中可以看出,不管是哪种实现,实例化的时候,都必须传一个EventExecutor数组。现在又有两个问题。

这个chooser是在何时被实例化的?
这个EventExecutor对象的具体类型是什么?

至此,总共有三个问题未解决

MultithreadEventLoopGroup类中的next方法返回的EventExecutor对象的实际类型是什么?
MultithreadEventLoopGroup类中的chooser属性是在何时被实力化的?
③实例化chooser属性时传入的EventExecutor数组的具体类型是什么?

带着这三个问题,回到我们自己编写的服务器启动端代码,在编写启动端代码时,会创建的两个实例对象。

EventLoopGroup bossGroup = new NioEventLoopGroup();

EventLoopGroup workerGroup = new NioEventLoopGroup();

在实例化NioEventLoopGroup的时候,会发现,程序会执行这样一段代码

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

去掉多余的代码,我们只需要关注下面这两行代码

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

chooser = chooserFactory.newChooser(children);

会发现MultithreadEventLoopGroup类中的chooser属性是在初始化NioEventLoopGroup的时候被实例化的,参数是children,至此,问题②解决。所以现在只需要关注children到底是什么类型的数组,只要解决了这个问题,那么问题①③都解决了。所以现在只需要关注第一行代码。newChild(executor, args),找到newChild方法,会发现是一个抽象方法,查看它的具体实现。
newChild方法的具体实现
因为我们此时实例化的是NioEventLoopGroup对象,所以我们直接找到NioEventLoopGroupnewChild方法。

@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
    EventLoopTaskQueueFactory queueFactory = args.length == 4 ? (EventLoopTaskQueueFactory) args[3] : null;
    return new NioEventLoop(this, executor, (SelectorProvider) args[0],
        ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2], queueFactory);
}

从这段代码中可以看出,children是一个NioEventLoop数组,所以,所以上文中的EventExecutor对象的实际类型是NioEventLoop,所以next().register(channel)其实NioEventLoop.register(channel),找到NioEventLoop类,可以在它的父类SingleThreadEventLoop找到register,方法如下

@Override
public ChannelFuture register(Channel channel) {
     return register(new DefaultChannelPromise(channel, this));
 }

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

上面这段代码中,只需要关注一行代码(此时上文的channel实际类型是NioServerSocketChannel

promise.channel().unsafe().register(this, promise);

用逐步分析的方式,从register(new DefaultChannelPromise(channel, this))这行代码可以看出,promise内部包裹了一个channel,所以这行代码的实际含义是NioServerSocketChannel.unsafe().register(this, promise);找到NioServerSocketChannel类,可以在它的父类AbstractChannel中找到unsafe方法,该方法返回了一个Unsafe对象,该对象是在实例化NioServerSocketChannel的被实例化的,这里只贴出具体位置和类型,不做具体描述(有兴趣的可以看一下源码)。

io.netty.channel.AbstractChannel
protected AbstractChannel(Channel parent, ChannelId id) {
    this.parent = parent;
    this.id = id;
    unsafe = newUnsafe();
    pipeline = newChannelPipeline();
}

io.netty.channel.nio.AbstractNioMessageChannel
@Override
protected AbstractNioUnsafe newUnsafe() {
    return new NioMessageUnsafe();
}

所以NioServerSocketChannel.unsafe().register(this, promise)可以简化为NioMessageUnsafe.register(this, promise),我们找到NioMessageUnsafe类,在它的父类AbstractChannel.AbstractUnsafe类中可以找到register方法。

此时传入的EventLoop对象的实际类型是NioEventLoop,而ChannelPromise中包含一个NioServerSocketChannel实例对象)

@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
    if (eventLoop == null) {
        throw new NullPointerException("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里面的逻辑
    if (eventLoop.inEventLoop()) {
        register0(promise);
    } else {
        try {
            eventLoop.execute(new Runnable() {
                @Override
                public void run() {
                    register0(promise);
                }
            });
        } catch (Throwable t) {
            closeForcibly();
            closeFuture.setClosed();
            safeSetFailure(promise, t);
        }
    }
}

这段代码我们只需要关注一个地方就行了。

至于为何只走else里面的逻辑,是本人debug得出的,至于何时会触发if里面的逻辑,本人也不是很清楚,目前并未碰到

eventLoop.execute(new Runnable() {
    @Override
    public void run() {
        register0(promise);
    }
});

根据上文梳理的结果,可以得知,此时的eventLoop的实际类型是NioEventLoop,所以上述代码片段可以看作NioEventLoop.execute,传入了一个Runnable对象,而这个Runnable对象内部会调用一个register0方法,我们找到NioEventLoop类。

register0方法是将ChannelPromise中包含的NioServerSocketChannel实例对象注册到selector

可以在NioEventLoop类的父类SingleThreadEventExecutor中找到execute方法,代码如下

@Override
public void execute(Runnable task) {
    if (task == null) {
        throw new NullPointerException("task");
    }

    boolean inEventLoop = inEventLoop();
    //将task任务添加到taskQueue中,等待执行
    addTask(task);
    if (!inEventLoop) {
        startThread();
        if (isShutdown()) {
            boolean reject = false;
            try {
                if (removeTask(task)) reject = true;
            } catch (UnsupportedOperationException e) {
                // 空方法
            }
            if (reject) reject();
        }
    }

    if (!addTaskWakesUp && wakesUpForTask(task)) {
        wakeup(inEventLoop);
    }
}

上述代码片段只需要关注两行代码

addTask(task);

startThread();

第一行代码是将传进来的Runnable对象添加到taskQueue中,而第二行代码则比较关键,首先看一下startThread方法

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

这段代码并无任何复杂的逻辑,其核心是调用了doStartThread方法,转到doStartThread方法

private void doStartThread() {
    assert thread == null;
    executor.execute(() -> {
        thread = Thread.currentThread();
        if (interrupted) {
            thread.interrupt();
        }

        boolean success = false;
        updateLastExecutionTime();
        try {
            //启动NioEventLoop中的run()方法
            SingleThreadEventExecutor.this.run();
            success = true;
        } catch (Throwable t) {
            logger.warn("Unexpected exception from an event executor: ", t);
        } finally {
            for (;;) {
                int oldState = state;
                if (oldState >= ST_SHUTTING_DOWN || STATE_UPDATER.compareAndSet(
                        SingleThreadEventExecutor.this, oldState, ST_SHUTTING_DOWN)) {
                    break;
                }
            }

            // 检查在循环结束时是否调用了confirmShutdown()。
            if (success && gracefulShutdownStartTime == 0) {
                if (logger.isErrorEnabled()) {
                    logger.error("Buggy " + EventExecutor.class.getSimpleName() + " implementation; " +
                            SingleThreadEventExecutor.class.getSimpleName() + ".confirmShutdown() must " +
                            "be called before run() implementation terminates.");
                }
            }

            try {
                // 运行所有剩余的任务并关闭挂钩。
                for (;;) {
                    if (confirmShutdown()) {
                        break;
                    }
                }
            } finally {
                try {
                    //关闭selector
                    cleanup();
                } finally {
                    //移除线程的所有fastthreadlocal。避免因为阻塞导致JVM可能会终止运行。
                    // See https://github.com/netty/netty/issues/6596.
                    FastThreadLocal.removeAll();

                    STATE_UPDATER.set(SingleThreadEventExecutor.this, ST_TERMINATED);
                    threadLock.countDown();
                    if (logger.isWarnEnabled() && !taskQueue.isEmpty()) {
                        logger.warn("An event executor terminated with " +
                                "non-empty task queue (" + taskQueue.size() + ')');
                    }
                    terminationFuture.setSuccess(null);
                }
            }
        }
    });
}

这段代码中一行核心的代码

SingleThreadEventExecutor.this.run();

这行代码的作用便是启动NioEventLoop类中的run方法,找到NioEventLoop类中的run方法(该方法是一个死循环)。

@Override
//死循环监听、处理事件
protected void run() {
    for (;;) {
        try {
            try {
                switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
                case SelectStrategy.CONTINUE:
                    continue;
                case SelectStrategy.BUSY_WAIT:
                case SelectStrategy.SELECT:
                    select(wakenUp.getAndSet(false));
                    if (wakenUp.get()) selector.wakeup();
                default:
                }
            } catch (IOException e) {
                // 如果选择器发生IO异常,开始重新构建选择器
                // the selector and retry. https://github.com/netty/netty/issues/8566
                rebuildSelector0();
                handleLoopException(e);
                continue;
            }

            cancelledKeys = 0;
            needsToSelectAgain = false;
            final int ioRatio = this.ioRatio;
            if (ioRatio == 100) {
                try {
                    processSelectedKeys();
                } finally {
                    // 执行所有的task任务
                    runAllTasks();
                }
            } else {
                final long ioStartTime = System.nanoTime();
                try {
                    processSelectedKeys();
                } finally {
                    final long ioTime = System.nanoTime() - ioStartTime;
                    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);
        }
    }
}

至此,已经找到了服务器启动的核心代码(NioEventLoop类中的run方法),这个方法首先会遍历当前NioEventLoopselector,判断是否有事件发生,第二步执行processSelectedKeys方法,判断事件类型,然后处理事件,第三步执行runAllTasks方法,该方法的作用就是执行taskQueue中的任务(比如之前提及的register0方法),执行完毕后移除。
下面就看一下processSelectedKeys方法和runAllTasks方法

processSelectedKeys方法

private void processSelectedKeys() {
    if (selectedKeys != null) {
        //不用JDK的selector.selectedKeys(), 性能更好(1%-2%),垃圾回收更少
        processSelectedKeysOptimized();
    } else {
        processSelectedKeysPlain(selector.selectedKeys());
    }
}

上述代码片段并未任何复杂的逻辑,再看一下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;

        //呼应于channel的register中的this: 例如:selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
        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;
        }
    }
}

该方法便是根据事件类型,执行processSelectedKey(k, (AbstractNioChannel) a)或者processSelectedKey(k, task)方法,具体逻辑如下。

processSelectedKey(k, (AbstractNioChannel) a)方法

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 || eventLoop == null) {
            return;
        }
        // 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
        // to a spin loop
        //处理读请求(断开连接)或接入连接
        if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
            unsafe.read();
        }
    } catch (CancelledKeyException ignored) {
        unsafe.close(unsafe.voidPromise());
    }
}

此处核心关注点是unsafe.read()这行代码,NioUnsaferead()只有两种具体实现,所以该行代码便是根据unsafe类型来进行是接收请求或者读取数据。具体实现如下


io.netty.channel.nio.AbstractNioChannel.AbstractNioUnsafe(接收请求)
@Override
public void read() {
    assert eventLoop().inEventLoop();
    final ChannelConfig config = config();
    final ChannelPipeline pipeline = pipeline();
    final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
    allocHandle.reset(config);

    boolean closed = false;
    Throwable exception = null;
    try {
        try {
            do {
           		// 接收请求
                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;
            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();
        }
    }
}

io.netty.channel.nio.AbstractNioByteChannel.NioByteUnsafe (读取数据)
@Override
public final void read() {
    final ChannelConfig config = config();
    if (shouldBreakReadReady(config)) {
        clearReadPending();
        return;
    }
    final ChannelPipeline pipeline = pipeline();
    final ByteBufAllocator allocator = config.getAllocator();
    //io.netty.channel.DefaultChannelConfig中设置RecvByteBufAllocator,默认AdaptiveRecvByteBufAllocator
    final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle();
    allocHandle.reset(config);

    ByteBuf byteBuf = null;
    boolean close = false;
    try {
        do {
            //尽可能分配合适的大小:guess
            byteBuf = allocHandle.allocate(allocator);
            //读并且记录读了多少,如果读满了,下次continue的话就直接扩容。
            allocHandle.lastBytesRead(doReadBytes(byteBuf));
            if (allocHandle.lastBytesRead() <= 0) {
                // nothing was read. release the buffer.
                byteBuf.release();
                byteBuf = null;
                close = allocHandle.lastBytesRead() < 0;
                if (close) {
                    // There is nothing left to read as we received an EOF.
                    readPending = false;
                }
                break;
            }

            allocHandle.incMessagesRead(1);
            readPending = false;
            //pipeline上执行,业务逻辑的处理就在这个地方
            pipeline.fireChannelRead(byteBuf);
            byteBuf = null;
        } while (allocHandle.continueReading());

        //记录这次读事件总共读了多少数据,计算下次分配大小。
        allocHandle.readComplete();
        //相当于完成本次读事件的处理
        pipeline.fireChannelReadComplete();

        if (close) {
            closeOnRead(pipeline);
        }
    } catch (Throwable t) {
        handleReadException(pipeline, byteBuf, t, close, allocHandle);
    } 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();
        }
    }
}

上述代码片段中doReadMessages(readBuf)便是用来接收请求的,具体逻辑如下

@Override
protected int doReadMessages(List<Object> buf) throws Exception {
    //接受新连接创建SocketChannel
    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;
}

processSelectedKey(k, task)方法

private static void processSelectedKey(SelectionKey k, NioTask<SelectableChannel> task) {
    int state = 0;
    try {
        task.channelReady(k.channel(), k);
        state = 1;
    } catch (Exception e) {
        k.cancel();
        invokeChannelUnregistered(task, k, e);
        state = 2;
    } finally {
        switch (state) {
        case 0:
            k.cancel();
            invokeChannelUnregistered(task, k, null);
            break;
        case 1:
            if (!k.isValid()) { // Cancelled by channelReady()
                invokeChannelUnregistered(task, k, null);
            }
            break;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值