Netty源码解析

Netty源码解析

示例

public class NettyServer {
    public static void main(String[] args) throws InterruptedException {
    	// 创建EventLoopGroup
        NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
        	// 2.创建ServerBootstrap,并进行配置
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new NettyServerHandler());
                        }
                    });
            System.out.println("server is ready...");
            // 3.绑定端口6666,获取ChannelFuture 
            ChannelFuture cf = serverBootstrap.bind(6666).sync();
            cf.channel().closeFuture().sync();
            cf.addListener((future -> {
                if (cf.isSuccess()) {
                    System.out.println("bind successfully");
                } else {
                    System.out.println("bind failed");
                }
            }));
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("Thread---"+Thread.currentThread().getName());
        System.out.println("ctx---"+ctx);
        Channel channel = ctx.channel();
        ChannelPipeline pipeline = ctx.pipeline();
        ByteBuf byteBuffer = (ByteBuf)msg;
        System.out.println("msg---"+byteBuffer.toString(CharsetUtil.UTF_8));
        System.out.println("address---"+channel.remoteAddress());
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.copiedBuffer("coplete...", CharsetUtil.UTF_8));
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}
public class NettyClient {
    public static void main(String[] args) throws InterruptedException {
        NioEventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new NettyClientHandler());
                }
            });
            ChannelFuture future = bootstrap.connect("127.0.0.1", 6668);
            future.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }
}
public class NettyClientHandler  extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("client---"+ctx);
        ctx.writeAndFlush(Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8));
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf)msg;
        System.out.println("msg---"+byteBuf);
        System.out.println("address---"+ctx.channel().remoteAddress());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}

NioEventLoopGroup的创建

首先是NioEventLoopGroup的创建
lNioEventLoopGroup是整个Netty的核心,NioEventLoopGroup可以看成是一组线程,里面每个EventLoop就是其中一个线程,每个EventLoop又会有一个Selector,循环监听注册在他上面的Channel的事件

EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();

构造方法会有很深的调用链,一直调用到父类的MultithreadEventLoopGroup
io.netty.channel.MultithreadEventLoopGroup#MultithreadEventLoopGroup(int, java.util.concurrent.Executor, java.lang.Object…)

static {
		// 默认线程一个NioEventLoopGroup启动的线程数为 cup核心数*2,一个线程代表一个EventLoop
        DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
                "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));

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

    /**
     * @see MultithreadEventExecutorGroup#MultithreadEventExecutorGroup(int, Executor, Object...)
     */
    protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
    	// 如果指定了线程,则使用指定的线程数,否则使用默认线程cup*2
        super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
    }
    protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
    	// DefaultEventExecutorChooserFactory 默认的 EventExecutorChooserFactory类型
        this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args);
    }

io.netty.util.concurrent.MultithreadEventExecutorGroup#MultithreadEventExecutorGroup(int, java.util.concurrent.Executor, io.netty.util.concurrent.EventExecutorChooserFactory, java.lang.Object…)

    /**
     * 创建一个实例
     *
     * @param nThreads          此实例将使用的线程数。
     * @param executor          要使用的执行器,或{@code null}(如果应该使用默认值)。
     * @param chooserFactory    要使用的{@link EventExecutorChooserFactory}。
     * @param args              将传递给每个{@link #newChild(Executor, Object…)}调用的参数
     */
    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());
        }
		//创建指定线程数的EventLoop数组
        children = new EventExecutor[nThreads];

        for (int i = 0; i < nThreads; i ++) {
            boolean success = false;
            try {
            	// 遍历创建NioEventLoop
            	// NioEventLoop是顶层接口,NioEventLoop是EventExecutor接口的间接实现类
                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 ++) {
                    	// 创建失败,则关闭所有的NioEventLoop
                        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);
                }
            }
        };
		//为每一个单例线程池(NioEventLoop)添加一个关闭监听器
        for (EventExecutor e: children) {
            e.terminationFuture().addListener(terminationListener);
        }
		//将所有的单例线程池添加到成员变量readonlyChildren (一个 HashSet) 中。
		// 所以表明,一个NioEventLoopGroup包含一组NioEventLoop
        Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
        Collections.addAll(childrenSet, children);
        readonlyChildren = Collections.unmodifiableSet(childrenSet);
    }

从上面启动函数可以看出,创建NioEventLoopGroup,默认一个NioEventLoopGroup会启动cpu核心数*2个线程(其实是一个单例线程池),每个线程对应一个NioEventLoop。一个NioEventLoop对应一个Selector,一个taskQueue

ServerBootstrap 创建和配置

ServerBootstrap是一个引导类,继承了父类AbstractBootstrap,用于引导Netty的配置和启动。
相关属性
io.netty.bootstrap.ServerBootstrap

	// 关于workerGroup的TCP 参数
    private final Map<ChannelOption<?>, Object> childOptions = new LinkedHashMap<ChannelOption<?>, Object>();
    private final Map<AttributeKey<?>, Object> childAttrs = new LinkedHashMap<AttributeKey<?>, Object>();
    private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);
    // workerGroup本身(这里是NioEventLoopGroup)
    private volatile EventLoopGroup childGroup;
    // 给workerGroup配置的ChannelHandler,用于处理发生在SocketChannel上的事件(读写)
    private volatile ChannelHandler childHandler;

io.netty.bootstrap.AbstractBootstrap

	// bossGroup本身(这里是NioEventLoopGroup)
    volatile EventLoopGroup group;
    @SuppressWarnings("deprecation")
    // 用于反射创建Channel的过程(这里的channel是NioServerSocketChannel)
    private volatile ChannelFactory<? extends C> channelFactory;
    // 绑定的端口的Socket地址对象
    private volatile SocketAddress localAddress;
    // 关于bossGroup的TCP 参数
    private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
    private final Map<AttributeKey<?>, Object> attrs = new LinkedHashMap<AttributeKey<?>, Object>();
    // 给bossGroup配置的ChannelHandler,用于处理发生在ServerSocketChannel的事件(连接)
    private volatile ChannelHandler handler;

ServerBootstrap的构造器是个空构造
io.netty.bootstrap.ServerBootstrap#ServerBootstrap()

public ServerBootstrap() { }

配置两个NioEventLoopGroup到

serverBootstrap.group(bossGroup, workerGroup)

io.netty.bootstrap.ServerBootstrap#group(io.netty.channel.EventLoopGroup, io.netty.channel.EventLoopGroup)

    public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
        super.group(parentGroup);
		...
        // workerGroup保存在ServerBootstrap 中的成员变量childGroup中
        this.childGroup = childGroup;
        return this;
    }

io.netty.bootstrap.AbstractBootstrap#group(io.netty.channel.EventLoopGroup)

    public B group(EventLoopGroup group) {
		...
        // bossgroup配置在父类成员变量group
        this.group = group;
        return self();
    }

配置bossGroup的通道类型,这里是NioServerSocketChannel
io.netty.bootstrap.AbstractBootstrap#channel

	/ * *
	* 用于创建实例的{@link Channel}{@link}* 您可以使用this{@link #channelFactory(io.netty.channel.ChannelFactory)},如果您需要
	* {@link Channel}实现没有无参数构造函数。
	* /
    public B channel(Class<? extends C> channelClass) {
        ...
        // 根据传进来的Channel类型创建channel工厂
        // 工厂类型为ReflectiveChannelFactory,代表是基于反射的方式去创建Channel的
        return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
    }

io.netty.bootstrap.AbstractBootstrap#channelFactory(io.netty.channel.ChannelFactory<? extends C>)

    public B channelFactory(io.netty.channel.ChannelFactory<? extends C> channelFactory) {
        return channelFactory((ChannelFactory<C>) channelFactory);
    }

io.netty.bootstrap.AbstractBootstrap#channelFactory(io.netty.bootstrap.ChannelFactory<? extends C>)

    @Deprecated
    public B channelFactory(ChannelFactory<? extends C> channelFactory) {
		...
		// 根据传进来的Channel类型创建channel工厂,并保存到成员变量中。
        this.channelFactory = channelFactory;
        return self();
    }

为bossGroup配置一些参数
io.netty.bootstrap.AbstractBootstrap#option

    /**
     * Allow to specify a {@link ChannelOption} which is used for the {@link Channel} instances once they got
     * created. Use a value of {@code null} to remove a previous set {@link ChannelOption}.
     */
    public <T> B option(ChannelOption<T> option, T value) {
        if (option == null) {
            throw new NullPointerException("option");
        }
        if (value == null) {
            synchronized (options) {
                options.remove(option);
            }
        } else {
            synchronized (options) {
            	// 保存配置到成员遍历options中(一个 LinkedHashMap)
                options.put(option, value);
            }
        }
        return self();
    }

为WorkerGroup配置一些参数
io.netty.bootstrap.ServerBootstrap#childOption

    public <T> ServerBootstrap childOption(ChannelOption<T> childOption, T value) {
        if (childOption == null) {
            throw new NullPointerException("childOption");
        }
        if (value == null) {
            synchronized (childOptions) {
                childOptions.remove(childOption);
            }
        } else {
            synchronized (childOptions) {
            	// 保存配置到成员变量childOptions中(一个 LinkedHashMap)
                childOptions.put(childOption, value);
            }
        }
        return this;
    }

为bossGroup配置Handler(ChannelHandler)
io.netty.bootstrap.AbstractBootstrap#handler(io.netty.channel.ChannelHandler)

    public B handler(ChannelHandler handler) {
        if (handler == null) {
            throw new NullPointerException("handler");
        }
        this.handler = handler;
        return self();
    }

为workerGroup配置Handler(ChannelHandler)
io.netty.bootstrap.ServerBootstrap#childHandler(io.netty.channel.ChannelHandler)

    public ServerBootstrap childHandler(ChannelHandler childHandler) {
        if (childHandler == null) {
            throw new NullPointerException("childHandler");
        }
        // 保存ChannelHandler 到成员变量childHandler 中,这里是一个ChannelInitializer
        // ChannelInitializer 也是Channel处理器中的一种,只是作用比较特殊
        // 主要用于追加其他的ChannelHandler,自身本身并不做业务处理
        this.childHandler = childHandler;
        return this;
    }

端口绑定,启动服务端

上面只是做了启动需要的配置,还没真正启动。在这里才会创建Channel并且绑定端口,返回ChannelFuture。这里的Channel是NioServerSocketChannel,他间接实现了接口Channel,NioServerSocketChannel的创建依赖于刚刚ServerBootstrap的配置时,通过NioServerSocketChannel的Class对象创建的ChannelFactory,他会反射创建NioServerSocketChannel。而ChannelFuture可以看成是Future,因为他也间接实现了Future,这里Channel的初始化和绑定端口是一个异步的动作,所以要返回一个Future。

serverBootstrap.bind(6666).sync();

io.netty.bootstrap.AbstractBootstrap#bind(int)

	/**
     * 创建NioServerSocketChannel并绑定端口.
     */
    public ChannelFuture bind(int inetPort) {
    	// 端口号封装成InetSocketAddress
        return bind(new InetSocketAddress(inetPort));
    }
 	/**
     * 创建NioServerSocketChannel并绑定端口.
     */
    public ChannelFuture bind(SocketAddress localAddress) {
		...
        // 调用dobing方法
        return doBind(localAddress);
    }

io.netty.bootstrap.AbstractBootstrap#doBind

    private ChannelFuture doBind(final SocketAddress localAddress) {
    	// 初始化并且注册NioServerSocketChannel,然后返回ChannelFuture
    	// ChannelFuture 可以看成就是 Future,实际上他间接实现了future接口
    	// 真正类型是 DefaultChannelPromise
        final ChannelFuture regFuture = initAndRegister();
        // 通过ChannelFuture 获取 Channel(NioServerSocketChannel)
        final Channel channel = regFuture.channel();
        // 初始化NioServerSocketChannel,不绑定端口,直接返回
        if (regFuture.cause() != null) {
            return regFuture;
        }

        if (regFuture.isDone()) {
            // 至此,我们知道注册是完整和成功的。
            ChannelPromise promise = channel.newPromise();
            // 执行 doBind0 方法,完成对端口的绑定
            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;
        }
    }

NioServerSocketChannel的创建和初始化

io.netty.bootstrap.AbstractBootstrap#initAndRegister

final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
        	// 利用ReflectiveChannelFactory反射创建NioServerSocketChannel
            channel = channelFactory.newChannel();
            // 初始化NioServerSocketChannel
            init(channel);
        } catch (Throwable t) {
            if (channel != null) {
                channel.unsafe().closeForcibly();
                return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
            }
            return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
        }
		// 注册NioServerSocketChannel并返回ChannelFuture 对象
        ChannelFuture regFuture = config().group().register(channel);
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }
        // 返回ChannelFuture 对象
        return regFuture;
    }
NioServerSocketChannel的创建

先看反射实例化的过程channel = channelFactory.newChannel();
ReflectiveChannelFactory是通过NioServerSocketChannel的无参构造反射实例化ReflectiveChannelFactory的,
io.netty.channel.ReflectiveChannelFactory#newChannel

    @Override
    public T newChannel() {
        try {
            return clazz.getConstructor().newInstance();
        } catch (Throwable t) {
            throw new ChannelException("Unable to create Channel from class " + clazz, t);
        }
    }

NioServerSocketChannel的无参构造,无参构造会调用重载的构造。在这之前会先调用newSocket方法,获取jdk自带的ServerSocketChannel实现类ServerSocketChannelImpl。然后再交给重载的构造,对原生的ServerSocketChannel进行包装,变成NioServerSocketChannel

    public NioServerSocketChannel() {
        this(newSocket(DEFAULT_SELECTOR_PROVIDER));
    }

io.netty.channel.socket.nio.NioServerSocketChannel#newSocket

	private static ServerSocketChannel newSocket(SelectorProvider provider) {
        try {
            return provider.openServerSocketChannel();
        } catch (IOException e) {
            throw new ChannelException("Failed to open a server socket.", e);
        }
    }

sun.nio.ch.SelectorProviderImpl#openServerSocketChannel
可以看到返回的就是ServerSocketChannelImpl类型的Channel

    public ServerSocketChannel openServerSocketChannel() throws IOException {
        return new ServerSocketChannelImpl(this);
    }

获取到的ServerSocketChannelImpl,通过重载的构造进行包装

    public NioServerSocketChannel(ServerSocketChannel channel) {
        super(null, channel, SelectionKey.OP_ACCEPT);
        // 创建了一个 NioServerSocketChannelConfig 对象,用于对外展示一些配置
        config = new NioServerSocketChannelConfig(this, javaChannel().socket());
    }

上面的第一行调用父类构造的代码,会一步一步调用到这里
io.netty.channel.AbstractChannel#AbstractChannel(io.netty.channel.Channel)

    protected AbstractChannel(Channel parent) {
    	// 保存ServerSocketChannelImpl对象为当parent属性
        this.parent = parent;
        // 创建一个唯一的 ChannelId
        id = newId();
        // 创建一个 NioMessageUnsafe,用于操作消息
        unsafe = newUnsafe();
        // 创建一个pipeline( DefaultChannelPipeline) ,一个Channel对应一个pipeline 
        // pipeline 是个双向链表结构,用于过滤所有的进出的消息
        pipeline = newChannelPipeline();
    }

反射实例化的过程到这里结束了,返回的是一个包装了原生ServerSocketChannelImpl的包装对象NioServerSocketChannel。
接下来看Channel的初始化init(channel);

NioServerSocketChannel的初始化

init 方法是 AbstractBootstrap 类的抽象方法,由子类 ServerBootstrap 实现
io.netty.bootstrap.ServerBootstrap#init

    @Override
    void init(Channel channel) throws Exception {
        final Map<ChannelOption<?>, Object> options = options0();
        // 设置 NioServerSocketChannel 的 的 TCP  属性
        // 由于 LinkedHashMap  是非线程安全的,使用同步进行处理
        synchronized (options) {
            setChannelOptions(channel, options, logger);
        }

        final Map<AttributeKey<?>, Object> attrs = attrs0();
        synchronized (attrs) {
            for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
                @SuppressWarnings("unchecked")
                AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
                channel.attr(key).set(e.getValue());
            }
        }
		
		// 获取绑定在NioServerSocketChannel上的pipeline
        ChannelPipeline p = channel.pipeline();

        final EventLoopGroup currentChildGroup = childGroup;
        final ChannelHandler currentChildHandler = childHandler;
        final Entry<ChannelOption<?>, Object>[] currentChildOptions;
        final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
        synchronized (childOptions) {
            currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));
        }
        synchronized (childAttrs) {
            currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));
        }
		// 对 NioServerSocketChannel 的 ChannelPipeline 添加 ChannelInitializer  处理器
		// pipeline 是一个双向循环链表,本身有head 和 tail两个节点,
		// 这里把ChannelInitializer  添加到 tail 的前面,tail 永远会在后面做一些系统的固定工作
        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(final Channel ch) throws Exception {
                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));
                    }
                });
            }
        });
    }

下面看调用ChannelPipeline 的addLast所做的工作
addLast 方法在 DefaultChannelPipeline 类中 是 pipeline 的核心,
里面会创建 一 个 AbstractChannelHandlerContext 对象 然后添加到pipeline所管理的链表的尾部前一个节点 。ChannelHandlerContext 对 象 是ChannelHandler 和 ChannelPipeline 之间的关联,每当有 ChannelHandler 添加到 Pipeline 中时,都会创建Context。Context 的主要功能是管理他所关联的 Handler 和同一个 Pipeline 中的其他 Handler 之间的交互。
io.netty.channel.DefaultChannelPipeline#addLast(io.netty.util.concurrent.EventExecutorGroup, java.lang.String, io.netty.channel.ChannelHandler)

    @Override
    public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
    	// 这里group 和 name 为空
        final AbstractChannelHandlerContext newCtx;
        synchronized (this) {
            checkMultiplicity(handler);
			// 包装一个ChannelHandlerContext对象
            newCtx = newContext(group, filterName(name, handler), handler);

			// 插入到当前pipeline的链表中
            addLast0(newCtx);

            if (!registered) {
                newCtx.setAddPending();
                callHandlerCallbackLater(newCtx, true);
                return this;
            }

            EventExecutor executor = newCtx.executor();
            if (!executor.inEventLoop()) {
                newCtx.setAddPending();
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        callHandlerAdded0(newCtx);
                    }
                });
                return this;
            }
        }
        // 最后,同步或者异步或者晚点异步的调用 callHandlerAdded0 方法
        callHandlerAdded0(newCtx);
        return this;
    }

可以看到这里就是插入到tail的前面
io.netty.channel.DefaultChannelPipeline#addLast0

    private void addLast0(AbstractChannelHandlerContext newCtx) {
        AbstractChannelHandlerContext prev = tail.prev;
        newCtx.prev = prev;
        newCtx.next = tail;
        prev.next = newCtx;
        tail.prev = newCtx;
    }

端口绑定

NioServerSocketChannel的创建和初始化已经看完了(也就是initAndRegister()方法),然后是端口绑定
io.netty.bootstrap.AbstractBootstrap#doBind0

    private static void doBind0(
            final ChannelFuture regFuture, final Channel channel,
            final SocketAddress localAddress, final ChannelPromise promise) {
        // 这里channel就是刚刚额NioServerSocketChannel
        // 通过NioServerSocketChannel获取NioEventLoop
        // 添加一个任务Runnable到当前EventLoop的taskQueue中
        channel.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                if (regFuture.isSuccess()) {
                	// 会调用到NioServerSocketChannel的bind方法
                    channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
                } else {
                    promise.setFailure(regFuture.cause());
                }
            }
        });
    }

最后会调用到NioServerSocketChannel的bind方法

    @Override
    protected void doBind(SocketAddress localAddress) throws Exception {
        if (PlatformDependent.javaVersion() >= 7) {
        	// javaChannel返回的是Nio原生的Channel,然后绑定端口
            javaChannel().bind(localAddress, config.getBacklog());
        } else {
            javaChannel().socket().bind(localAddress, config.getBacklog());
        }
    }

最终会注册一个 Accept 事件等待客户端的连接
然后最后就会进入到NioEventLoop里面,进行循环的监听了

    @Override
    protected void run() {
        for (;;) {
            try {
                switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
                    case SelectStrategy.CONTINUE:
                        continue;
                    case SelectStrategy.SELECT:
                    	// Selector.select
                        select(wakenUp.getAndSet(false));
                        if (wakenUp.get()) {
                            selector.wakeup();
                        }
                    default:
                }

                cancelledKeys = 0;
                needsToSelectAgain = false;
                final int ioRatio = this.ioRatio;
                if (ioRatio == 100) {
                    try {
                    	// 处理selectedKeys,也就是监听到的事件
                        processSelectedKeys();
                    } finally {
                        // 执行所有的任务.
                        runAllTasks();
                    }
                } else {
                    final long ioStartTime = System.nanoTime();
                    try {
                        processSelectedKeys();
                    } finally {
                        // Ensure we always run tasks.
                        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);
            }
        }
    }

接收客户端连接请求,注册Channel到workGroup中

上面已经启动了服务端,现在EventLoop 中就可以接收客户端的连接了
EventLoop 的作用是一个死循环,而这个循环中做 3 件事情:

  • 有条件的等待 Nio 事件 select(wakenUp.getAndSet(false));
  • 处理 Nio 事件。processSelectedKeys();
  • 处理消息队列中的任务。runAllTasks();
    启动客户端,触发连接事件。当监听到连接事件时,会进入以下代码

io.netty.channel.nio.NioEventLoop#processSelectedKey(java.nio.channels.SelectionKey, io.netty.channel.nio.AbstractNioChannel)

private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
	...
	// readyOps 为16,代表accept事件
    if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
    	// 调用unsafe的read方法
    	// unsafe 是 boss 线程中 NioServerSocketChannel 的 AbstractNioMessageChannel$NioMessageUnsafe 对象
        unsafe.read();
   }	
}

io.netty.channel.nio.AbstractNioMessageChannel.NioMessageUnsafe#read

        @Override
        public void read() {
        	//  检查该 eventloop 线程是否是当前线程
            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 {
                    	// 执行 doReadMessages 方法,并传入一个 readBuf 变量,
                    	// 这个变量是一个 List,也就是容器,
                    	//  doReadMessages 是读取 boss 线程中的 
                    	// NioServerSocketChannel 接受到的请求。并把这些请求放进容器
                        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();
                // 遍历容器
                // 循环调用 ServerSocket 的 pipeline 的 fireChannelRead 方法, 
                // 开始执行 管道中的
                for (int i = 0; i < size; i ++) {
                    readPending = false;
                    // 循环遍历 容器中的所有请求,
                    // 调用 pipeline 的 fireChannelRead 方法,
                    // 用于处理这些接受的请求或者其他事件,
handler 的 ChannelRead 方法
                    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 {
                if (!readPending && !config.isAutoRead()) {
                    removeReadOp();
                }
            }
        }

进入doReadMessages方法,他做的事情就是通过原生的serverSocketChannel 的accept 方法,获取到SocketChannel,然后封装成NioSocketChannel后,放入到容器buf中
io.netty.channel.socket.nio.NioServerSocketChannel#doReadMessages

    @Override
    protected int doReadMessages(List<Object> buf) throws Exception {
    	// 通过工具类,调用 NioServerSocketChannel 内部
    	// 封装的 serverSocketChannel 的 accept 方法
    	// 获取到一个 JDK 的 SocketChannel。
        SocketChannel ch = SocketUtils.accept(javaChannel());

        try {
            if (ch != null) {
            	// 然后,使用 NioSocketChannel 进行封装
            	// 把 NioSocketChannel 添加到buf容器中
                buf.add(new NioSocketChannel(this, ch));
                return 1;
            }
        } catch (Throwable t) {
            ...
        }
        return 0;
    }

回到 read 方法,继续分析 循环执行 pipeline.fireChannelRead 方法
前面分析 doReadMessages 方法的作用是通过 ServerSocket 的 accept 方法获取到 Tcp 连接,然后封装成Netty 的 NioSocketChannel 对象,最后添加到 容器中。
在 read 方法中,循环调用 ServerSocket 的 pipeline 的 fireChannelRead 方法, 开始执行 管道中的 handler 的 ChannelRead 方法
可以看到会反复执行多个 handler 的 ChannelRead ,我们知道,pipeline 里面又 4 个 handler ,分别是 Head ,LoggingHandler ,ServerBootstrapAcceptor ,Tail
我们重点看看 ServerBootstrapAcceptor
io.netty.channel.AbstractChannelHandlerContext#invokeChannelRead(java.lang.Object)

    private void invokeChannelRead(Object msg) {
        if (invokeHandler()) {
            try {
            	// 调用handler的channelRead方法
                ((ChannelInboundHandler) handler()).channelRead(this, msg);
            } catch (Throwable t) {
                notifyHandlerException(t);
            }
        } else {
            fireChannelRead(msg);
        }
    }

进入到ServerBootstrapAcceptor的channelRead方法,可以看到调用了wokerGroup的register方法,注册了该Channel

        @Override
        @SuppressWarnings("unchecked")
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
        	// 刚刚中获取的NioSocketChannel
            final Channel child = (Channel) msg;
			// 给当前 NioSocketChannel 的管道添加之前配置的 ChannelInitializer
            child.pipeline().addLast(childHandler);
			// 设置 NioSocketChannel 的各种属性
            setChannelOptions(child, childOptions, logger);

            for (Entry<AttributeKey<?>, Object> e: childAttrs) {
                child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
            }

            try {
            	//将NioSocketChannel 注册到 worker 线程池(最开始创建的workerGroup)
            	// 并添加一个监听器
                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);
            }
        }

因为workerGroup的类型是NioEventLoopGroup,继承了MultithreadEventLoopGroup,所以workerGroup的register方法首先进入MultithreadEventLoopGroup的register方法
io.netty.channel.MultithreadEventLoopGroup#register(io.netty.channel.Channel)

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

next方法会返回一个NioEventLoop
io.netty.util.concurrent.DefaultEventExecutorChooserFactory.PowerOfTwoEventExecutorChooser#next

        @Override
        public EventExecutor next() {
            return executors[idx.getAndIncrement() & executors.length - 1];
        }

接着往下…,会到
io.netty.channel.AbstractChannel.AbstractUnsafe#register

        @Override
        public final void register(EventLoop eventLoop, final ChannelPromise promise) {
        	...
        	// 直接看这一行,其他全省略
			register0(promise);
			...
        }

然后到beginRead(),然后doBeginRead(),都在AbstractUnsafe类的内部
直到最后会到AbstractNioChannel的doBeginRead
io.netty.channel.nio.AbstractNioChannel#doBeginRead

    @Override
    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) {
            selectionKey.interestOps(interestOps | readInterestOp);
        }
    }

执行到这里时,针对于这个客户端的连接就完成了,接下来就可以监听读事件了

Netty 接受请求过程

接受连接----->创建一个新的 NioSocketChannel----------->注册到一个 worker EventLoop 上-------->
注册 selecot Read 事件。

  1. 服务器轮询 Accept 事件,获取事件后调用 unsafe 的 read 方法,这个 unsafe 是 ServerSocket 的内部类,该方法内部由 2 部分组成
  2. doReadMessages 用于创建 NioSocketChannel 对象,该对象包装 JDK 的 Nio Channel 客户端。该方法会像创建 ServerSocketChanel 类似创建相关的 pipeline , unsafe,config
  3. 随后执行 执行 pipeline.fireChannelRead 方法,并将自己绑定到一个 chooser 选择器选择的 workerGroup 中的一个 EventLoop。并且注册一个 0,表示注册成功,但并没有注册读(1)事件

ChannelPipeline,ChannelHandler,ChannelHandlerContext的创建

Netty 中的 ChannelPipeline 、 ChannelHandler 和 ChannelHandlerContext 是非常核心的组件
三者关系

  1. 每当 ServerSocket 创建一个新的连接,就会创建一个 Socket,对应的就是目标客户端。
  2. 每一个新创建的 Socket 都将会分配一个全新的 ChannelPipeline(以下简称 pipeline)
  3. 每一个 ChannelPipeline 内部都含有多个 ChannelHandlerContext(以下简称 Context)
  4. 他们一起组成了双向链表,这些 Context 用于包装我们调用 addLast 方法时添加的 ChannelHandler(以下简称handler)

ChannelSocket 和 ChannelPipeline 是一对一的关联关系,而 pipeline 内部的多个 Context 形成了链
表,Context 对 只是对 Handler 的封装。
当一个请求进来的时候,会进入 Socket 对应的 pipeline,并经过 pipeline 所有的 handler,对,就是设计模式中的过滤器模式。

ChannelPipeline

io.netty.channel.ChannelPipeline

public interface ChannelPipeline
        extends ChannelInboundInvoker, ChannelOutboundInvoker, Iterable<Entry<String, ChannelHandler>> {

ChannelPipeline接口继承了 inBound,outBound,Iterable 接口,表示他可以调用 数据出站的方法和入站的方法,同时也能遍历内部的链表, 看看他的几个代表性的方法,基本上都是针对 handler 链表的插入,追加,删除,替换操作,类似是一个 LinkedList。同时,也能返回 channel(也就是 socket)

在 pipeline 的接口文档上,提供了一幅图

* <pre>
 *                                                 I/O Request
 *                                            via {@link Channel} or
 *                                        {@link ChannelHandlerContext}
 *                                                      |
 *  +---------------------------------------------------+---------------+
 *  |                           ChannelPipeline         |               |
 *  |                                                  \|/              |
 *  |    +---------------------+            +-----------+----------+    |
 *  |    | Inbound Handler  N  |            | Outbound Handler  1  |    |
 *  |    +----------+----------+            +-----------+----------+    |
 *  |              /|\                                  |               |
 *  |               |                                  \|/              |
 *  |    +----------+----------+            +-----------+----------+    |
 *  |    | Inbound Handler N-1 |            | Outbound Handler  2  |    |
 *  |    +----------+----------+            +-----------+----------+    |
 *  |              /|\                                  .               |
 *  |               .                                   .               |
 *  | ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()|
 *  |        [ method call]                       [method call]         |
 *  |               .                                   .               |
 *  |               .                                  \|/              |
 *  |    +----------+----------+            +-----------+----------+    |
 *  |    | Inbound Handler  2  |            | Outbound Handler M-1 |    |
 *  |    +----------+----------+            +-----------+----------+    |
 *  |              /|\                                  |               |
 *  |               |                                  \|/              |
 *  |    +----------+----------+            +-----------+----------+    |
 *  |    | Inbound Handler  1  |            | Outbound Handler  M  |    |
 *  |    +----------+----------+            +-----------+----------+    |
 *  |              /|\                                  |               |
 *  +---------------+-----------------------------------+---------------+
 *                  |                                  \|/
 *  +---------------+-----------------------------------+---------------+
 *  |               |                                   |               |
 *  |       [ Socket.read() ]                    [ Socket.write() ]     |
 *  |                                                                   |
 *  |  Netty Internal I/O Threads (Transport Implementation)            |
 *  +-------------------------------------------------------------------+
 * </pre>
  • 这是一个 handler 的 list,handler 用于处理或拦截入站事件和出站事件,pipeline 实现了过滤器的高级形式,以便用户控制事件如何处理以及 handler 在 pipeline 中如何交互。
  • 上图描述了一个典型的 handler 在 pipeline 中处理 I/O 事件的方式,IO 事件由 inboundHandler 或者outBoundHandler 处理,并通过调用 ChannelHandlerContext.fireChannelRead 方法转发给其最近的处理程序 。
  • 入站事件由入站处理程序以自下而上的方向处理,如图所示。入站处理程序通常处理由图底部的 I / O 线程生成入站数据。入站数据通常从如 SocketChannel.read(ByteBuffer) 获取。
  • 个 通常一个 e pipeline 个 有多个 handler,例如,一个典型的服务器在每个通道的管道中都会有以下处理程序
    协议解码器 - 将二进制数据转换为 Java 对象。
    协议编码器 - 将 Java 对象转换为二进制数据。
    业务逻辑处理程序 - 执行实际业务逻辑(例如数据库访问)
  • 你的业务程序不能将线程阻塞,会影响 IO 的速度,进而影响整个 Netty 程序的性能。如果你的业务程序很快,就可以放在 IO 线程中,反之,你需要异步执行。或者在添加 handler 的时候添加一个线程池,例如:
    下面这个任务执行的时候,将不会阻塞 IO 线程,执行的线程来自 group 线程池
    pipeline.addLast(group,“handler”,new MyBusinessLogicHandler());

io.netty.channel.ChannelHandlerContext#fireChannelRead

@Override
ChannelHandlerContext fireChannelRead(Object msg);

io.netty.channel.AbstractChannelHandlerContext#fireChannelRead

    @Override
    public ChannelHandlerContext fireChannelRead(final Object msg) {
    	// invokeChannelRead处理Channel的入站事件
    	// 寻找下一个Inbound类型的ChannelHandlerContext
        invokeChannelRead(findContextInbound(), msg);
        return this;
    }

io.netty.channel.AbstractChannelHandlerContext#findContextInbound

    private AbstractChannelHandlerContext findContextInbound() {
        AbstractChannelHandlerContext ctx = this;
        do {
        	// context的next获取下一个context,查看是否是Inbound类型,是则返回
            ctx = ctx.next;
        } while (!ctx.inbound);
        return ctx;
    }

出站的时候则相反
io.netty.channel.AbstractChannelHandlerContext#findContextOutbound

    private AbstractChannelHandlerContext findContextOutbound() {
        AbstractChannelHandlerContext ctx = this;
        do {
            ctx = ctx.prev;
        } while (!ctx.outbound);
        return ctx;
    }

所以Context是通过inbound和outbond两个boolean值标识他是出站还是入站的Context

ChannelHandler

io.netty.channel.ChannelHandler

public interface ChannelHandler {

    /**
     * 当把 ChannelHandler 添加到 pipeline 时被调用
     */
    void handlerAdded(ChannelHandlerContext ctx) throws Exception;

    /**
     * 当从 pipeline 中移除时调用
     */
    void handlerRemoved(ChannelHandlerContext ctx) throws Exception;

    /**
     * 当处理过程中在 pipeline 发生异常时调用
     */
    @Deprecated
    void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;

}

ChannelHandler 的作用就是处理 IO 事件或拦截 IO 事件,并将其转发给下一个处理程序ChannelHandler。
Handler 处理事件时分入站和出站的,两个方向的操作都是不同的,因此,Netty 定义了两个子接口继承
ChannelHandler

  • ChannelInboundHandler 入站事件接口
    • channelActive 用于当 Channel 处于活动状态时被调用;
    • channelRead 当从 Channel 读取数据时被调用等等方法。
    • 程序员需要重写一些方法,当发生关注的事件,需要在方法中实现我们的业务逻辑,因为当事件发生时,Netty 会回调对应的方法。
  • ChannelOutboundHandler 出站事件接口
    • bind 方法,当请求将 Channel 绑定到本地地址时调用
    • close 方法,当请求关闭 Channel 时调用等等
    • 出站操作都是一些连接和写出数据类似的方法。
  • ChannelDuplexHandler 处理出站和入站事件
    • ChannelDuplexHandler 间接实现了入站接口并直接实现了出站接口。
    • 是一个通用的能够同时处理入站事件和出站事件的类。

ChannelHandlerContext

ChannelHandlerContext 继承了出站方法调用接口和入站方法调用接口ChannelOutboundInvoker 和 ChannelInboundInvoker
这两个 invoker 就是针对入站或出站方法来的,就是在 入站或出站 handler 的外层再包装一层,达到在方法前后拦截并做一些特定操作的目的
ChannelHandlerContext 不仅仅时继承了他们两个的方法,同时也定义了一些自己的方法
这些方法能够获取 Context 上下文环境中对应的比如 channel,executor,handler ,pipeline,内存分配器,关联的 handler 是否被删除。
Context 就是包装了 handler 相关的一切,以方便 Context 可以在 pipeline 方便的操作 handler

ChannelPipeline | ChannelHandler | ChannelHandlerContext 创建过程

分为 3 个步骤来看创建的过程:

  • 任何一个 ChannelSocket 创建的同时都会创建 一个 pipeline。
  • 当用户或系统内部调用 pipeline 的 add*** 方法添加 handler 时,都会创建一个包装这 handler 的 Context。
  • 这些 Context 在 pipeline 中组成了双向链表。

在上面分析NioServerSocketChannel创建的时候可以看到pipeline的创建
io.netty.channel.AbstractChannel#AbstractChannel(io.netty.channel.Channel)

    protected AbstractChannel(Channel parent) {
    	// 保存ServerSocketChannelImpl对象为当parent属性
        this.parent = parent;
        // 创建一个唯一的 ChannelId
        id = newId();
        // 创建一个 NioMessageUnsafe,用于操作消息
        unsafe = newUnsafe();
        // 创建一个pipeline( DefaultChannelPipeline) ,一个Channel对应一个pipeline 
        // pipeline 是个双向链表结构,用于过滤所有的进出的消息
        pipeline = newChannelPipeline();
    }

沿着newChannelPipeline方法追踪到达
io.netty.channel.DefaultChannelPipeline#DefaultChannelPipeline

    protected DefaultChannelPipeline(Channel channel) {
    	// 此时channel还是空的,经过这一步后,channel就被赋值为NioServerSocketChannel类型
        this.channel = ObjectUtil.checkNotNull(channel, "channel");
        // 创建一个 future 和 promise,用于异步回调使用
        succeededFuture = new SucceededChannelFuture(channel, null);
        voidPromise =  new VoidChannelPromise(channel, true);
		
		// ChannelPipeline的尾部节点的ChannelHandlerContext, 一个 inbound 的 tailContext
        tail = new TailContext(this);
        // ChannelPipeline的头部节点的ChannelHandlerContext
        // 一个既是 inbound 类型又是 outbound 类型的 headContext
        head = new HeadContext(this);

		// hean和tail连接起来形成双向链表
        head.next = tail;
        tail.prev = head;
    }

pipeline添加Handler时创建Context
io.netty.channel.DefaultChannelPipeline#addLast(io.netty.util.concurrent.EventExecutorGroup, java.lang.String, io.netty.channel.ChannelHandler)

    @Override
    public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
        final AbstractChannelHandlerContext newCtx;
        synchronized (this) {
        	// 检查这个 handler 实例是否是共享的,如果不是,并且已经被别的 pipeline 使用了,则抛出异常。
            checkMultiplicity(handler);
			// 调用 newContext(group, filterName(name, handler), handler)  方法 ,个 创建一个 Context。 。 从这里可以看出来了 ,个 每次添加一个 handler 联 都会创建一个关联 Context。
            newCtx = newContext(group, filterName(name, handler), handler);
			//  调用 addLast 将 方法,将 Context  追加到链表中。
            addLast0(newCtx);

			// 如果这个通道还没有注册到 selecor 上,
			// 就将这个 Context 添加到这个 pipeline 的待办任务中。
			// 当注册好了以后,就会调用 callHandlerAdded0 方法(默认是什么都不做,用户可以实现这个方法)。
            if (!registered) {
                newCtx.setAddPending();
                callHandlerCallbackLater(newCtx, true);
                return this;
            }

            EventExecutor executor = newCtx.executor();
            if (!executor.inEventLoop()) {
                newCtx.setAddPending();
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        callHandlerAdded0(newCtx);
                    }
                });
                return this;
            }
        }
       
        callHandlerAdded0(newCtx);
        return this;
    }

Pipeline Handler HandlerContext 创建过程梳理

到这里,针对三对象创建过程,了解的差不多了,和最初说的一样,每当创建 ChannelSocket 的时候都会创建一个绑定的 pipeline,一对一的关系,创建 pipeline 的时候也会创建 tail 节点和 head 节点,形成最初的链表。tail是入站 inbound 类型的 handler, head 既是 inbound 也是 outbound 类型的 handler。在调用 pipeline 的 addLast方法的时候,会根据给定的 handler 创建一个 Context,然后,将这个 Context 插入到链表的尾端(tail 前面)。到此就 OK 了

  1. 每当创建 ChannelSocket 的时候都会创建一个绑定的 pipeline,一对一的关系,创建 pipeline 的时候也会创建tail 节点和 head 节点,形成最初的链表。
  2. 在调用 pipeline 的 addLast 方法的时候,会根据给定的 handler 创建一个 Context,然后,将这个 Context 插入到链表的尾端(tail 前面)。
  3. Context 包装 handler,多个 Context 在 pipeline 中形成了双向链表
  4. 入站方向叫 inbound,由 head 节点开始,出站方法叫 outbound ,由 tail 节点开始

ChannelPipeline 调度 handler 的源码剖析

入站相关方法

当一个请求进来的时候,会第一个调用 pipeline 的 相关方法,如果是入站事件,这些方法由 fire 开头,
表示开始管道的流动。让后面的 handler 继续处理
ChannelPipeline的真实类型是DefaultChannelPipeline,当有入站事件发生时,就会首先触发ChannelPipeline中的fireChannelActive方法,然后执行其他的firexxx方法。这些方法都是 inbound 的方法。里面是一个静态方法的调用,会调用 head 的 ChannelInboundInvoker 接口的方法,再然后调用 handler 的真正方法
io.netty.channel.DefaultChannelPipeline#fireChannelActive

    @Override
    public final ChannelPipeline fireChannelActive() {
        AbstractChannelHandlerContext.invokeChannelActive(head);
        return this;
    }

这里挑一个fireChannelRead方法看一下,启动浏览器,再启动客户端。可以看到调用到了
fireChannelRead。
io.netty.channel.DefaultChannelPipeline#fireChannelRead

    @Override
    public final ChannelPipeline fireChannelRead(Object msg) {
    	// 调用AbstractChannelHandlerContext的静态方法
    	// head是当前pipeline的头结点ChannelHandlerContext
    	// msg 类型是NioSocketChannel
        AbstractChannelHandlerContext.invokeChannelRead(head, msg);
        return this;
    }

io.netty.channel.AbstractChannelHandlerContext#invokeChannelRead(io.netty.channel.AbstractChannelHandlerContext, java.lang.Object)

    static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
        final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
        // 获取NioEventLoop,Context -> pipeline -> channel -> NioEventLoop
        EventExecutor executor = next.executor();
        // 判断当前线程是否是本NioEventLoop
        // 是则直接在本线程执行操作
        if (executor.inEventLoop()) {
        	// 执行next的invokeChannelRead,第一次进来是head
            next.invokeChannelRead(m);
        } else {
        	// executor 不是本线程,则调用 execute 方法异步执行
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    next.invokeChannelRead(m);
                }
            });
        }
    }

next是AbstractChannelHandlerContext,而当前也是通过AbstractChannelHandlerContext的静态方法调用进来的,所以invokeChannelRead方法会进入到当前类的invokeChannelRead方法,而invokeChannelRead就会取到与当前Context关联的Handler,执行对应的方法(这里是channelRead)。但是当前Context是头结点head,head获取handler的操作较特殊,他会返回自身 (return this;)。
io.netty.channel.AbstractChannelHandlerContext#invokeChannelRead(java.lang.Object)

    private void invokeChannelRead(Object msg) {
        if (invokeHandler()) {
            try {
            	// 从当前 Context 中取出与之关联的 Handler,并强转成ChannelInboundHandler,
            	// 然后执行对应handler的相关操作方法,如channelRead。
            	// 当前 ChannelHandlerContext 是 hear,
            	// 而 hear 的真实类型是 DefaultChannelPipeline 的内部类 HeadContext,
            	// HeadContext 的 handler()里面是return this,所以还是当前 hear 对象。
            	// HeadContext 除了继承了父类AbstractChannelHandlerContext,也实现了
            	// ChannelOutboundHandler, ChannelInboundHandler两个接口,
            	// 所以他既是Context,也是handler。
            	// 调用hear的channelRead方法,进入HeadContext的channelRead方法
                ((ChannelInboundHandler) handler()).channelRead(this, msg);
            } catch (Throwable t) {
                notifyHandlerException(t);
            }
        } else {
            fireChannelRead(msg);
        }
    }

io.netty.channel.DefaultChannelPipeline.HeadContext#channelRead

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            // ctx 还是 head,msg还是NIOSocketChannel
            ctx.fireChannelRead(msg);
        }

上面方法中,ctx 还是 head,msg 还是 NIOSocketChannel,所以会调用 hear 的 fireChannelRead 方法,进入到 AbstractChannelHandlerContext 的 fireChannelRead 方法。也就是说现在是从 DefaultChannelPipeline 的 fireChannelRead,到了HeadContext 的 fireChannelRead
io.netty.channel.AbstractChannelHandlerContext#fireChannelRead

    @Override
    public ChannelHandlerContext fireChannelRead(final Object msg) {
        invokeChannelRead(findContextInbound(), msg);
        return this;
    }

findContextInbound方法是找到链表中下一个处理入站事件的ChannelHandlerContext

    private AbstractChannelHandlerContext findContextInbound() {
        AbstractChannelHandlerContext ctx = this;
        do {
            ctx = ctx.next;
        } while (!ctx.inbound);
        return ctx;
    }

io.netty.channel.AbstractChannelHandlerContext#invokeChannelRead(io.netty.channel.AbstractChannelHandlerContext, java.lang.Object)

    static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
        final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeChannelRead(m);
        } else {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    next.invokeChannelRead(m);
                }
            });
        }
    }

所以这里又回到了AbstractChannelHandlerContext的invokeChannelRead方法,只是现在的next不是head节点了,而是下一个inbound的ChannelHandlerContext,并且取出的handler也是真正和context关联的handler(return handler;),就会调用到我们注册进workerGroup的handler的相关方法。以此类推,就循环调用了链表上所有的inbound相关的ChannelHandlerContext

出站相关方法

io.netty.channel.DefaultChannelPipeline#bind(java.net.SocketAddress, io.netty.channel.ChannelPromise)

    @Override
    public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
        return tail.bind(localAddress, promise);
    }

可以看到出站首先会调用tail节点

小结

pipeline 首先会调用 Context 的静态方法 fireXXX,并传入 Context。然后,静态方法调用 Context 的 invoker 方法,而 invoker 方法内部会调用该 Context 所包含的 Handler 的真正的 XXX 方法,调用结束后,如果还需要继续向后传递,就调用 Context 的 fireXXX2 方法,循环往复。

Netty心跳服务

Netty 作为一个网络框架,提供了诸多功能,比如编码解码等,Netty 还提供了非常重要的一个服务-----心跳机制 heartbeat。我们需要在心跳检测Handle(如IdleStateHandler)后添加一个自定义的Handler,重写userEventTiggered方法,用于接收心跳检测Handler的处理结果(如IdleStateEvent),进行相应的处理。通过心跳检查对方是否有效,这是 RPC 框架中是必不可少的功能。

IdleStateHandler的4个重要属性

private final boolean observeOutput; //是否考虑出站时较慢的情况。默认值是 false
private final long readerIdleTimeNanos;//读事件空闲时间,0 则禁用事件
private final long writerIdleTimeNanos;//写事件空闲时间,0 则禁用事件
private final long allIdleTimeNanos;//读或写空闲时间,0 则禁用事件

当该 handler 被添加到 pipeline 中时,则调用handlerAdded方法,进而调用 initialize 方法
io.netty.handler.timeout.IdleStateHandler#handlerAdded

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        if (ctx.channel().isActive() && ctx.channel().isRegistered()) {
        	 initialize(ctx);
        } else {
        }
    }

进入initialize方法
io.netty.handler.timeout.IdleStateHandler#initialize

    private void initialize(ChannelHandlerContext ctx) {
        switch (state) {
        case 1:
        case 2:
            return;
        }

        state = 1;
        // 调用 initOutputChanged 方法,初始化 “监控出站数据属性”
        initOutputChanged(ctx);

        lastReadTime = lastWriteTime = ticksInNanos();
        // 只要给定的参数大于 0,就创建一个定时任务,
        // 每个事件都创建。同时,将 state 状态设置为 1,防止重复初始化。
        if (readerIdleTimeNanos > 0) {
        	// 这里的 schedule 方法会调用 eventLoop 的 schedule 方法,将定时任务添加进队列中
        	// 然后返回一个ScheduledFuture类型的对象,是一个Future
        	// 保存到 IdleStateHandler 的成员变量
            readerIdleTimeout = schedule(ctx, new ReaderIdleTimeoutTask(ctx),
                    readerIdleTimeNanos, TimeUnit.NANOSECONDS);
        }
        if (writerIdleTimeNanos > 0) {
        	// writerIdleTimeout 与 readerIdleTimeout 一样,一个ScheduledFuture对象
            writerIdleTimeout = schedule(ctx, new WriterIdleTimeoutTask(ctx),
                    writerIdleTimeNanos, TimeUnit.NANOSECONDS);
        }
        if (allIdleTimeNanos > 0) {
       		// allIdleTimeout 也是 ScheduledFuture对象
            allIdleTimeout = schedule(ctx, new AllIdleTimeoutTask(ctx),
                    allIdleTimeNanos, TimeUnit.NANOSECONDS);
        }
    }

上面调用initOutputChanged方法会初始化上面罗列的IdleStateHandler4个重要属性,然后根据3个超时时间是否大于0,创建定时任务。而ReaderIdleTimeoutTask,WriterIdleTimeoutTask,AllIdleTimeoutTask 均属于IdleStateHandler 的 内部类。这三个任务类共有一个父类(AbstractIdleTask,也是内部类)。这个父类提供了一
个模板方法

io.netty.handler.timeout.IdleStateHandler.AbstractIdleTask

	/* AbstractIdleTask 实现了Runnable 接口 */
    private abstract static class AbstractIdleTask implements Runnable {

		// 与当前handler绑定的Context
        private final ChannelHandlerContext ctx;

        AbstractIdleTask(ChannelHandlerContext ctx) {
            this.ctx = ctx;
        }
		
		/* 给子类提供的模板方法 */
        @Override
        public void run() {
        	// 通道关闭则不执行后序操作
            if (!ctx.channel().isOpen()) {
                return;
            }
			// 否则会执行子类重写的抽象方法run(ctx)
            run(ctx);
        }
		
		// 要求子类重写的run(ctx)抽象方法
        protected abstract void run(ChannelHandlerContext ctx);
    }

下面就逐个看看Task子类的重写的run(ChannelHandlerContext ctx) 方法

ReaderIdleTimeoutTask 的 run 方法

io.netty.handler.timeout.IdleStateHandler.ReaderIdleTimeoutTask#run

        @Override
        protected void run(ChannelHandlerContext ctx) {
        	// 得到用户设置的超时时间
            long nextDelay = readerIdleTimeNanos;
            if (!reading) {
            	// 如果读取操作结束了(执行了 channelReadComplete 方法设置) ,	
            	// 就用用户设置的超时时间 减去 
            	// 系统当前的时间(纳秒)给定时间和最后一次读操作的时间的差值
            	//(执行了 channelReadComplete 方法设置)
                nextDelay -= ticksInNanos() - lastReadTime;
            }
			// 如果 nextDelay 小于0,就触发事件
            if (nextDelay <= 0) {
            	// 首先将任务再次放到队列,时间是刚开始设置的时间,
            	// 返回一个 promise 对象,用于做取消操作
                readerIdleTimeout = schedule(ctx, this, readerIdleTimeNanos, TimeUnit.NANOSECONDS);

				// 设置 first 属性为 false ,表示,下一次读取不再是第一次了,
				// 这个属性在 channelRead 方法会被改成 true
                boolean first = firstReaderIdleEvent;
                firstReaderIdleEvent = false;

                try {
                	// 创建一个 IdleStateEvent 类型的写事件对象,
                    IdleStateEvent event = newIdleStateEvent(IdleState.READER_IDLE, first);
                    // 将IdleStateEvent 对象传递给用户的 UserEventTriggered 方法。
                    // 完成触发事件的操作
                    channelIdle(ctx, event);
                } catch (Throwable t) {
                    ctx.fireExceptionCaught(t);
                }
            } else {
                // 反之,继续放入队列, 间隔时间是新的计算时间
                readerIdleTimeout = schedule(ctx, this, nextDelay, TimeUnit.NANOSECONDS);
            }
        }

总的来说,每次读取操作都会记录一个时间,定时任务时间到了,会计算当前时间和最后一次读的时间
的间隔,如果间隔超过了设置的时间,就触发 UserEventTriggered 方法
io.netty.handler.timeout.IdleStateHandler#channelIdle

    protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception {
    	// 触发 接下来的 Handler 的 UserEventTriggered 方法
    	// 调用流程与上面分析的 fireChannelRead 一样
        ctx.fireUserEventTriggered(evt);
    }

WriterIdleTimeoutTask 的 run 方法

写任务的 run 代码逻辑基本和读任务的逻辑一样,唯一不同的就是有一个针对 出站较慢数据的判断
hasOutputChanged

io.netty.handler.timeout.IdleStateHandler.WriterIdleTimeoutTask#run

        @Override
        protected void run(ChannelHandlerContext ctx) {

            long lastWriteTime = IdleStateHandler.this.lastWriteTime;
            long nextDelay = writerIdleTimeNanos - (ticksInNanos() - lastWriteTime);
            if (nextDelay <= 0) {
                writerIdleTimeout = schedule(ctx, this, writerIdleTimeNanos, TimeUnit.NANOSECONDS);

                boolean first = firstWriterIdleEvent;
                firstWriterIdleEvent = false;

                try {
                	// 出站较慢数据的判断hasOutputChanged
                    if (hasOutputChanged(ctx, first)) {
                        return;
                    }

                    IdleStateEvent event = newIdleStateEvent(IdleState.WRITER_IDLE, first);
                    channelIdle(ctx, event);
                } catch (Throwable t) {
                    ctx.fireExceptionCaught(t);
                }
            } else {
                writerIdleTimeout = schedule(ctx, this, nextDelay, TimeUnit.NANOSECONDS);
            }
        }

AllIdleTimeoutTask 的 run

表示这个监控着所有的事件。当读写事件发生时,都会记录。代码逻辑和写事件的的基本一致

io.netty.handler.timeout.IdleStateHandler.AllIdleTimeoutTask#run

        @Override
        protected void run(ChannelHandlerContext ctx) {

            long nextDelay = allIdleTimeNanos;
            if (!reading) {
            	// 这里会取走后读时间和最后写时间的最大值
                nextDelay -= ticksInNanos() - Math.max(lastReadTime, lastWriteTime);
            }
            if (nextDelay <= 0) {
                allIdleTimeout = schedule(ctx, this, allIdleTimeNanos, TimeUnit.NANOSECONDS);

                boolean first = firstAllIdleEvent;
                firstAllIdleEvent = false;

                try {
                    if (hasOutputChanged(ctx, first)) {
                        return;
                    }

                    IdleStateEvent event = newIdleStateEvent(IdleState.ALL_IDLE, first);
                    channelIdle(ctx, event);
                } catch (Throwable t) {
                    ctx.fireExceptionCaught(t);
                }
            } else {
                // Either read or write occurred before the timeout - set a new
                // timeout with shorter delay.
                allIdleTimeout = schedule(ctx, this, nextDelay, TimeUnit.NANOSECONDS);
            }
        }

小结 Netty 的心跳机制

  1. IdleStateHandler 可以实现心跳功能,当服务器和客户端没有任何读写交互时,并超过了给定的时间,则会触发用户 handler 的 userEventTriggered 方法。用户可以在这个方法中尝试向对方发送信息,如果发送失败,则关闭连接。
  2. IdleStateHandler 的实现基于 EventLoop 的定时任务,每次读写都会记录一个值,在定时任务运行的时候,通过计算当前时间和设置时间和上次事件发生时间的结果,来判断是否空闲。
  3. 内部有 3 个定时任务,分别对应读事件,写事件,读写事件。通常用户监听读写事件就足够了。
  4. 同时,IdleStateHandler 内部也考虑了一些极端情况:客户端接收缓慢,一次接收数据的速度超过了设置的空闲时间。Netty 通过构造方法中的 observeOutput 属性来决定是否对出站缓冲区的情况进行判断。
  5. 如果出站缓慢,Netty 不认为这是空闲,也就不触发空闲事件。但第一次无论如何也是要触发的。因为第一次无法判断是出站缓慢还是空闲。当然,出站缓慢的话,可能造成 OOM , OOM 比空闲的问题更大。
  6. 所以,当你的应用出现了内存溢出,OOM 之类,并且写空闲极少发生(使用了 observeOutput 为 true),那么就需要注意是不是数据出站速度过慢。
  7. 还有一个注意的地方:就是 ReadTimeoutHandler ,它继承自 IdleStateHandler,当触发读空闲事件的时候,就触发 ctx.fireExceptionCaught 方法,并传入一个 ReadTimeoutException,然后关闭 Socket。
  8. 而 WriteTimeoutHandler 的实现不是基于 IdleStateHandler 的,他的原理是,当调用 write 方法的时候,会创建一个定时任务,任务内容是根据传入的 promise 的完成情况来判断是否超出了写的时间。当定时任务根据指定时间开始运行,发现 promise 的 isDone 方法返回 false,表明还没有写完,说明超时了,则抛出异常。当 write方法完成后,会打断定时任务。

Netty 核心组件 EventLoop

NioEventLoop间接实现了ScheduledExecutorService,EventLoop两个接口,有继承了父类SingleThreadEventExecutor

  1. ScheduledExecutorService 接口表示是一个定时任务接口,EventLoop 可以接受定时任务。
  2. EventLoop 接口:Netty 接口文档说明该接口作用:一旦 Channel 注册了,就处理该 Channel 对应的所有I/O 操作。
  3. SingleThreadEventExecutor 表示这是一个单个线程的线程池
  4. EventLoop 是一个单例的线程池,里面含有一个死循环的线程不断的做着 3 件事情:监听端口,处理端口事件,处理队列事件。每个 EventLoop 都可以绑定多个 Channel,而每个 Channel 始终只能由一个 EventLoop 来处理

io.netty.util.concurrent.SingleThreadEventExecutor#execute

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

        boolean inEventLoop = inEventLoop();
        // 首先判断该 EventLoop 的线程是否是当前线程
        if (inEventLoop) {
        	// 如果是,直接添加到任务队列中去
            addTask(task);
        } else {
        	// 如果不是,则尝试启动线程(但由于线程是单个的,因此只能启动一次)
            startThread();
            // 将任务添加到队列中去。
            addTask(task);
            // 如果线程已经停止,并且删除任务
            if (isShutdown() && removeTask(task)) {
            	// 则执行拒绝策略,默认是抛出异常
                reject();
            }
        }
		// 如果 addTaskWakesUp 是 false 并且任务不是 NonWakeupRunnable 类型的
        if (!addTaskWakesUp && wakesUpForTask(task)) {
          // 就尝试唤醒 selector。这个时候,阻塞在 selecor 的线程就会立即返回
          wakeup(inEventLoop);
        }
    }

先看任务入队的操作
io.netty.util.concurrent.SingleThreadEventExecutor#addTask

    protected void addTask(Runnable task) {
		...
		// 调用offerTask方法添加任务到队列
        if (!offerTask(task)) {
        	// 添加不成功,则执行拒绝策略
            reject(task);
        }
    }

io.netty.util.concurrent.SingleThreadEventExecutor#offerTask

    final boolean offerTask(Runnable task) {
    	// 判断当前线程池是否处于shutdown,shutdown状态不接收新任务
        if (isShutdown()) {
        	// 执行拒绝策略
            reject();
        }
        // 添加任务到队列
        return taskQueue.offer(task);
    }

还有第一次进来时当前线程不是EventLoop线程,会进行startThread方法启动一个线程,startThread又会调用到doStartThread方法

io.netty.util.concurrent.SingleThreadEventExecutor#doStartThread

    private void doStartThread() {
        assert thread == null;
        // 首先调用 executor 的 execute 方法
        // 这个 executor 就是在创建 Event LoopGroup 的时候创建的 ThreadPerTaskExecutor 类
        // 该 execute 方法会将 Runnable 包装成 Netty 的 FastThreadLocalThread
        executor.execute(new Runnable() {
            @Override
            public void run() {
                thread = Thread.currentThread();
                // 首先判断线程中断状态
                if (interrupted) {
                    thread.interrupt();
                }

                boolean success = false;
                // 设置最后一次的执行时间
                updateLastExecutionTime();
                try {
                	// 核心
                	// 执行当前 NioEventLoop 的 run 方法,
                	// 注意:这个方法是个死循环,是整个 EventLoop 的核心
                    SingleThreadEventExecutor.this.run();
                    success = true;
                } catch (Throwable t) {
                    logger.warn("Unexpected exception from an event executor: ", t);
                } finally {
                    for (;;) {
                        int oldState = state;
                        // 在 finally 块中,
                        // 使用 CAS 不断修改 state 状态,改成ST_SHUTTING_DOWN
                        // 也就是当线程 Loop 结束的时候,关闭线程。
                        // 最后还要死循环确认是否关闭,否则不会 break
                        if (oldState >= ST_SHUTTING_DOWN || STATE_UPDATER.compareAndSet(
                                SingleThreadEventExecutor.this, oldState, ST_SHUTTING_DOWN)) {
                            break;
                        }
                    }
                    if (success && gracefulShutdownStartTime == 0) {
                        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 {
                        	// 执行 cleanup 操作
                            cleanup();
                        } finally {
                        	// 更新状态为 ST_TERMINATED
                            STATE_UPDATER.set(SingleThreadEventExecutor.this, ST_TERMINATED);
                            // 释放当前线程锁
                            threadLock.release();
                            // 如果任务队列不是空,则打印队列中还有多少个未完成的任务。
                            if (!taskQueue.isEmpty()) {
                                logger.warn(
                                        "An event executor terminated with " +
                                                "non-empty task queue (" + taskQueue.size() + ')');
                            }
							// 并回调 terminationFuture 方法。
                            terminationFuture.setSuccess(null);
                        }
                    }
                }
            }
        });
    }

run方法会进入到NioEventLoop的run方法,进行死循环

io.netty.channel.nio.NioEventLoop#run

    @Override
    protected void run() {
        for (;;) {
            try {
                switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
                    case SelectStrategy.CONTINUE:
                        continue;
                    case SelectStrategy.SELECT:
                    	// 监听注册到Selector上面的所有的channel
                        select(wakenUp.getAndSet(false));
                        
                        if (wakenUp.get()) {
                            selector.wakeup();
                        }
                        // fall through
                    default:
                }

                cancelledKeys = 0;
                needsToSelectAgain = false;
                final int ioRatio = this.ioRatio;
                if (ioRatio == 100) {
                    try {
                    	// 监听到事件,处理SelectedKeys
                        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);
            }
            try {
                if (isShuttingDown()) {
                    closeAll();
                    if (confirmShutdown()) {
                        return;
                    }
                }
            } catch (Throwable t) {
                handleLoopException(t);
            }
        }
    }

select方法里面调用 selector 的 select 方法,默认阻塞一秒钟,如果有定时任务,则在定时任务剩余时间的基础上在加上 0.5秒进行阻塞。当执行 execute 方法的时候,也就是添加任务的时候,唤醒 selecor,防止 selecotr 阻塞时间过长

io.netty.channel.nio.NioEventLoop#select

    private void select(boolean oldWakenUp) throws IOException {
        Selector selector = this.selector;
        try {
            int selectCnt = 0;
            long currentTimeNanos = System.nanoTime();
            long selectDeadLineNanos = currentTimeNanos + delayNanos(currentTimeNanos);
            for (;;) {
                long timeoutMillis = (selectDeadLineNanos - currentTimeNanos + 500000L) / 1000000L;
                if (timeoutMillis <= 0) {
                    if (selectCnt == 0) {
                        selector.selectNow();
                        selectCnt = 1;
                    }
                    break;
                }

                if (hasTasks() && wakenUp.compareAndSet(false, true)) {
                    selector.selectNow();
                    selectCnt = 1;
                    break;
                }
				// Nio的操作,Selector的select方法,阻塞给定时间,默认一秒
                int selectedKeys = selector.select(timeoutMillis);
                selectCnt ++;
				//如果 1 秒后返回,有返回值 || select 被用户唤醒 || 任务队列有任务 || 有定时任务即将被执行; 则跳出循环
                if (selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks() || hasScheduledTasks()) {

                    break;
                }
                if (Thread.interrupted()) {

                    if (logger.isDebugEnabled()) {
                        logger.debug("Selector.select() returned prematurely because " +
                                "Thread.currentThread().interrupt() was called. Use " +
                                "NioEventLoop.shutdownGracefully() to shutdown the NioEventLoop.");
                    }
                    selectCnt = 1;
                    break;
                }

                long time = System.nanoTime();
                if (time - TimeUnit.MILLISECONDS.toNanos(timeoutMillis) >= currentTimeNanos) {

                    selectCnt = 1;
                } else if (SELECTOR_AUTO_REBUILD_THRESHOLD > 0 &&
                        selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) {

                    logger.warn(
                            "Selector.select() returned prematurely {} times in a row; rebuilding Selector {}.",
                            selectCnt, selector);

                    rebuildSelector();
                    selector = this.selector;
                    
                    selector.selectNow();
                    selectCnt = 1;
                    break;
                }

                currentTimeNanos = time;
            }

            if (selectCnt > MIN_PREMATURE_SELECTOR_RETURNS) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Selector.select() returned prematurely {} times in a row for Selector {}.",
                            selectCnt - 1, selector);
                }
            }
        } catch (CancelledKeyException e) {
            if (logger.isDebugEnabled()) {
                logger.debug(CancelledKeyException.class.getSimpleName() + " raised by a Selector {} - JDK bug?",
                        selector, e);
            }
        }
    }

小结

  • 每次执行 ececute 方法都是向队列中添加任务。当第一次添加时就启动线程,执行 run 方法,而 run 方法是整个 EventLoop 的核心,就像 EventLoop 的名字一样,Loop Loop ,不停的 Loop ,Loop 做什么呢?做 3 件事情。
  • 调用 selector 的 select 方法,默认阻塞一秒钟,如果有定时任务,则在定时任务剩余时间的基础上在加上 0.5秒进行阻塞。当执行 execute 方法的时候,也就是添加任务的时候,唤醒 selecor,防止 selecotr 阻塞时间过长。
  • 当 selector 返回的时候,回调用 processSelectedKeys 方法对 selectKey 进行处理。
  • 当 processSelectedKeys 方法执行结束后,则按照 ioRatio 的比例执行 runAllTasks 方法,默认是 IO 任务时间和非 IO 任务时间是相同的,你也可以根据你的应用特点进行调优 。比如 非 IO 任务比较多,那么你就将ioRatio 调小一点,这样非 IO 任务就能执行的长一点。防止队列积攒过多的任务。

handler 中加入线程池和 Context 中添加线程池

在 Netty 中做耗时的,不可预料的操作,比如数据库,网络请求,会严重影响 Netty 对 Socket 的处理速度。而解决方法就是将耗时任务添加到异步线程池中。但就添加线程池这步操作来讲,可以有 2 种方式,而且这 2种方式实现的区别也蛮大的。

  • 处理耗时业务的第一种方式—handler 中加入线程池
  • 处理耗时业务的第二种方式—Context 中添加线程池

处理耗时业务的第一种方式–handler

修改NettyServerHandler,添加异步处理逻辑

	// EventExecutorGroup 是当前的业务线程池,是jdk原生线程池的一种封装
	static final EventExecutorGroup GROUP = new DefaultEventExecutorGroup(10);
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        final Object copMsg = msg;
        final ChannelHandlerContext copCtx = ctx;
		
		// 提交异步处理任务到业务线程池
        GROUP.submit(new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                ByteBuf buf = (ByteBuf)copMsg;
                byte[] req = new byte[buf.readableBytes()];
                buf.readBytes(req);
                String body = new String(req, "UTF-8");
                Thread.sleep(10 * 1000);
                System.out.println(body + " " + Thread.currentThread().getName());
                String resStr = "hello i am server";
                ByteBuf resp = Unpooled.copiedBuffer(resStr.getBytes());
                copCtx.writeAndFlush(resp);
                return null;
            }
        });

        System.out.println("go on..");
   }

在 channelRead 方法,模拟了一个耗时 10 秒的操作,这里,我们将这个任务提交到了一个自定义的业
务线程池中,这样,就不会阻塞 Netty 的 IO 线程

io.netty.channel.AbstractChannelHandlerContext#write(java.lang.Object, boolean, io.netty.channel.ChannelPromise)

    private void write(Object msg, boolean flush, ChannelPromise promise) {
        AbstractChannelHandlerContext next = findContextOutbound();
        final Object m = pipeline.touch(msg, next);
        EventExecutor executor = next.executor();
        // 判断当前线程是否是下个 outbound 的 executor 线程不是当前线程
        if (executor.inEventLoop()) {
        	// 如果是,则在当前线程中直接执行操作
            if (flush) {
                next.invokeWriteAndFlush(m, promise);
            } else {
                next.invokeWrite(m, promise);
            }
        } else {
        	// 不是则封装成一个task
            AbstractWriteTask task;
            if (flush) {
                task = WriteAndFlushTask.newInstance(next, m, promise);
            }  else {
                task = WriteTask.newInstance(next, m, promise);
            }
            // 然后放入mpsc 队列中,等待 IO 任务执行完毕后执行队列中的任务
            safeExecute(executor, task, promise, m);
        }
    }

处理耗时业务的第二种方式-Context 中添加线程池

修改NettyServer,添加handler的时候指定线程池。然后 handler 中的代码就使用普通的方式来处理耗时业务。

            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
//                            ch.pipeline().addLast(new NettyServerHandler());
							// 当我们在调用 addLast 方法添加线程池后,
							// handler 将优先使用这个线程池,如果不添加,将使用 IO 线程
                            ch.pipeline().addLast(new DefaultEventExecutorGroup(16), new NettyServerHandler());
                        }
                    });

当走到 AbstractChannelHandlerContext 的 invokeChannelRead 方法的时候,executor.inEventLoop() 是不会通过的,因为当前线程是 IO 线程 Context (也就是 Handler)的 executor 是业务线程, 所以会异步执行
io.netty.channel.AbstractChannelHandlerContext#invokeChannelRead(io.netty.channel.AbstractChannelHandlerContext, java.lang.Object)

    static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
        final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeChannelRead(m);
        } else {
        	// 因为当前线程是IO线程,executor的线程是业务线程,所以上面的判断不会通过
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    next.invokeChannelRead(m);
                }
            });
        }
    }

两种方式的比较

  • 第一种方式在 handler 中添加异步,可能更加的自由,比如如果需要访问数据库,那我就异步,如果不需要,就不异步,异步会拖长接口响应时间。因为需要将任务放进 mpscTask 中。如果 IO 时间很短,task 很多,可能一个循环下来,都没时间执行整个 task,导致响应时间达不到指标。
  • 第二种方式是 Netty 标准方式(即加入到队列),但是,这么做会将整个 handler 都交给业务线程池。不论耗时不耗时,都加入到队列里,不够灵活。
  • 各有优劣,从灵活性考虑,第一种较好
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值