Netty源码解读

目录

 

netty版本:4.1.30.Final

四个基本事件

Selector是什么?

SelectionKey是什么?

NioServerSocketChannel源码

NioSocketChannel源码

NioEventLoop是什么?

netty服务端启动

服务端整体逻辑架构

参考文档:


netty版本:4.1.30.Final

 

四个基本事件

OP_READ = 1        00000001 读
OP_WRITE = 4       00000100 写
OP_CONNECT = 8     00001000 连接
OP_ACCEPT = 16     00010000 接受连接

如果一个SelectionKey感兴趣读,     那么其interestOps=00000001
如果一个SelectionKey感兴趣写,     那么其interestOps=00000100
如果一个SelectionKey感兴趣连接,   那么其interestOps=00001000
如果一个SelectionKey感兴趣接受连接,那么其interestOps=00010000

如果感兴趣多个事件:java运算符 与(&)、非(~)、或(|)、异或(^)

读\写:00000001 | 00000100 = 00000101

读\写\连接\接受连接:00000001 | 00000100 | 00001000 | 00010000 = 00011101

判断自己是否有关注某个事件:

int interestSet = selectionKey.interestOps();
 
boolean isInterestedAccept    = (interestSet & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;
boolean isInterestedConnect   = (interestSet & SelectionKey.OP_CONNECT) == SelectionKey.OP_CONNECT;
boolean isInterestedInRead    = (interestSet & SelectionKey.OP_READ) == SelectionKey.OP_READ;
boolean isInterestedInWrite   = (interestSet & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE;

 

Selector是什么?

它是一个抽象类,看它一个子类:SelectorImpl,它维护了一个Set(可以在selector上面注册多个socketchannel,selector会给每个channel返回一个selectionKey,该selectionKey就是标识该channel注册在某一个selector上)

Selector几个重载的select()方法: 

  • select():阻塞到至少有一个通道在你注册的事件上就绪了。 
  • select(long timeout):和select()一样,但最长阻塞事件为timeout毫秒。 
  • selectNow():非阻塞,只要有通道就绪就立刻返回(如果没有也立马返回)。
     

Selector的wakeUp方法:

  • 当发现任务队列里面没有任务要处理就会执行selector的阻塞方法select(timeout),如果这时候突然有外部线程调用channel去发送一个消息,那么netty会把改消息包装成一个任务放入队列,放入队列的时候netty会检测当前selector是否处于阻塞,如果是则调用wakeup方法让selector从阻塞中恢复过来进行loop的run方法执行。
     

SelectionKey是什么?

它是一个抽象类,看它一个子类:SelectionKeyImpl,看它的定义:

public class SelectionKeyImpl extends AbstractSelectionKey {
    final SelChImpl channel;
    public final SelectorImpl selector;
    private int index;
    private volatile int interestOps;
    private int readyOps;
    ......
}

可以看到,一个SelectionKey中包含一个channel、一个Selector、该channel感兴趣的事件集合interestOps,SelectionKey就是标识该channel注册在某一个selector上

 

NioServerSocketChannel源码


public class NioServerSocketChannel extends AbstractNioMessageChannel
        implements io.netty.channel.socket.ServerSocketChannel {


    @Override
    protected int doReadMessages(List buf) throws Exception {
        SocketChannel ch = javaChannel().accept();

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

}

首先通过ServerSocketChannel的accept()方法接受新的客户端请求,如果SocketChannel不为空,则利用当前的NioServerSocketChannel、EventLoop和SocketChannel创建新的NioSocketChannel,并加入到List buf中,最后返回1,标识服务端读取消息成功,对于NioServerSocketChannel,它的读取操作就是接受客户端连接请求,创建NioSocketChannel对象。

 

NioSocketChannel源码


public class NioSocketChannel extends AbstractNioByteChannel implements io.netty.channel.socket.SocketChannel {
    
    @Override
    protected SocketChannel javaChannel() {
        return (SocketChannel) super.javaChannel();
    }

    @Override
    protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
        if (localAddress != null) {
            javaChannel().socket().bind(localAddress);
        }

        boolean success = false;
        try {
            boolean connected = javaChannel().connect(remoteAddress);
            if (!connected) {
                selectionKey().interestOps(SelectionKey.OP_CONNECT);
            }
            success = true;
            return connected;
        } finally {
            if (!success) {
                doClose();
            }
        }
    }
}

先调用javaChannel().socket().bind(localAddress)绑定本地地址,若成功则:调用javaChannel().connect(remoteAddress)发起TCP连接

 

NioEventLoop是什么?

nioEventloop其内部(父类)持有一个thread对象,而该nioEventloop的run方法正是由该线程执行的

nioEventloop还持有两个最重要的对象--selector和queue,前者就是我们的多路复用器,后者则是存放我们的任务队列

分析nioEventloop的run方法:

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

            cancelledKeys = 0;
            needsToSelectAgain = false;
            final int ioRatio = this.ioRatio;
            if (ioRatio == 100) {
                try {
                    processSelectedKeys();
                } finally {
                    // Ensure we always run tasks.
                    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);
        }
    }
}
  • 第一步:先看switch里面的:selectStrategy.calculateStrategy(selectNowSupplier, hasTasks()):这是一个返回Int类型值的方法:calculateStrategy()方法里面的实现:return hasTasks ? selectSupplier.get() : SelectStrategy.SELECT;如果任务队列不为空,返回selectSupplier.get(),这个是get()是:return NioEventLoop.this.selectNow()  --> this.selector.selectNow(),NioEventLoop是什么已经介绍过selector.selectNow()
  • 若第一步返回的是SelectStrategy.SELECT,switch走到select(wakenUp.getAndSet(false));这代表当前没有可执行的任务,这个方法里面有一个非常著名的解决netty空轮询的判断:判断是否触发了jdk的selector的空轮旬bug,主要原理就是发现每次selector从阻塞方法中醒来既没有任务也没有io时间且阻塞的时间也没到我们设置的时间,如果一直重复这样到达一定的阈值,则我们就rebuildselector。详细分析见我的:Selector空轮询
  • 若第一步返回的是:selector.selectNow()>0,继续看下面的代码再看看下面的processSelectedKeys();这是真正处理每个channel的Io事件的方法;跟进去看:
        private void processSelectedKeysOptimized() {
            for (int i = 0; i < selectedKeys.size; ++i) {
                final SelectionKey k = selectedKeys.keys[i];
                // null out entry in the array to allow to have it GC'ed once the Channel close
                // See https://github.com/netty/netty/issues/2363
                selectedKeys.keys[i] = null;
    
                final Object a = k.attachment();
    
                if (a instanceof AbstractNioChannel) {
                    processSelectedKey(k, (AbstractNioChannel) a);
                } else {
                    @SuppressWarnings("unchecked")
                    NioTask task = (NioTask) 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;
                }
            }
        }

    遍历该EventLoop持有的selector的selectedKeys:最终调用的是:

    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) {
                    return;
                }
                if (eventLoop != this || eventLoop == null) {
                    return;
                }
                unsafe.close(unsafe.voidPromise());
                return;
            }
    
            try {
                int readyOps = k.readyOps();
                if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
                    int ops = k.interestOps();
                    ops &= ~SelectionKey.OP_CONNECT;
                    k.interestOps(ops);
                    unsafe.finishConnect();
                }
                if ((readyOps & SelectionKey.OP_WRITE) != 0) {
                    ch.unsafe().forceFlush();
                }
                if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
                    unsafe.read();
                }
            } catch (CancelledKeyException ignored) {
                unsafe.close(unsafe.voidPromise());
            }
        }

    这个就已经是我们熟悉的NIO中的类似操作读写连接事件的io
    处理逻辑里面还有一个else:processSelectedKey(k, task);暂时不看了,详情见:task执行

 

netty服务端启动

EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
    // 创建并初始化 Netty 服务端 Bootstrap 对象
    ServerBootstrap bootstrap = new ServerBootstrap();
    bootstrap.group(bossGroup, workerGroup);
    bootstrap.channel(NioServerSocketChannel.class);
    bootstrap.childHandler(new ChannelInitializer() {
        @Override
        public void initChannel(SocketChannel channel) throws Exception {
            ChannelPipeline pipeline = channel.pipeline();

            /**
             * 收到客户端请求后,先执行解码RpcDecoder,在进入RpcServerHandler执行
             * 执行完成后,由RpcEncoder编码后,返回给客户端
             */
            //我是RpcDecoder,属于ChannelInboundHandlerAdapter,先add先执行
            pipeline.addLast(new RpcDecoder(RpcRequest.class)); // 解码 RPC 请求

            //我是RpcEncoder,属于ChannelOutboundHandlerAdapter,先add却要后执行
            pipeline.addLast(new RpcEncoder(RpcResponse.class)); // 编码 RPC 响应

            //我是RpcServerHandler,属于ChannelInboundHandlerAdapter,先add先执行,所以我在RpcDecoder之后执行
            pipeline.addLast(new RpcServerHandler(handlerMap)); // 处理 RPC 请求
        }
    });
    bootstrap.option(ChannelOption.SO_BACKLOG, 1024);
    bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
    // 获取 RPC 服务器的 IP 地址与端口号
    String[] addressArray = StringUtil.split(serviceAddress, ":");
    String ip = addressArray[0];
    int port = Integer.parseInt(addressArray[1]);
    // 启动 RPC 服务器
    ChannelFuture future = bootstrap.bind(ip, port).sync();
    // 注册 RPC 服务地址
    if (serviceRegistry != null) {
        for (String interfaceName : handlerMap.keySet()) {
            serviceRegistry.register(interfaceName, serviceAddress);
            LOGGER.debug("register service: {} => {}", interfaceName, serviceAddress);
        }
    }
    LOGGER.debug("server started on port {}", port);
    // 关闭 RPC 服务器
    future.channel().closeFuture().sync();
} finally {
    workerGroup.shutdownGracefully();
    bossGroup.shutdownGracefully();
}

从ChannelFuture future = bootstrap.bind(ip, port).sync()开始分析:调用的是:AbstractBootstrap.bind,最后调用AbstractBootstrap的这个方法:

private ChannelFuture doBind(final SocketAddress localAddress) {
        final ChannelFuture regFuture = initAndRegister();
        final Channel channel = regFuture.channel();
        if (regFuture.cause() != null) {
            return regFuture;
        }

        if (regFuture.isDone()) {
            // At this point we know that the registration was complete and successful.
            ChannelPromise promise = channel.newPromise();
            doBind0(regFuture, channel, localAddress, promise);
            return promise;
        } else {
            // Registration future is almost always fulfilled already, but just in case it's not.
            final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
            regFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    Throwable cause = future.cause();
                    if (cause != null) {
                        // Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
                        // IllegalStateException once we try to access the EventLoop of the Channel.
                        promise.setFailure(cause);
                    } else {
                        // Registration was successful, so set the correct executor to use.
                        // See https://github.com/netty/netty/issues/2586
                        promise.registered();

                        doBind0(regFuture, channel, localAddress, promise);
                    }
                }
            });
            return promise;
        }
    }

看这里方法面的两个核心方法:initAndRegister(),以及doBind0(),先看initAndRegister():

    final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            channel = channelFactory.newChannel();
            init(channel);
        } catch (Throwable t) {}

        ChannelFuture regFuture = config().group().register(channel);
    
        return regFuture;
    }

initAndRegister主要做三个事情:new channel、init channel、register channel

  • newChannel():
    channelFactory是
        public B channel(Class channelClass) {
            if (channelClass == null) {
                throw new NullPointerException("channelClass");
            }
            return channelFactory(new ReflectiveChannelFactory(channelClass));
        }
    这个方法是:bootstrap.channel(NioServerSocketChannel.class),即channelFactory.newChannel()最终创建channel相当于调用默认构造函数new出一个 NioServerSocketChannel对象。NioServerSocketChannel的初始化还会涉及到newChannelPipeline等各种参数的初始化,后面再单独分析。
  • init(channel):由于是服务端的channel的init,具体方法实现我们找到ServerBootstrap.init
     
    public class ServerBootstrap extends AbstractBootstrap {
        
        @Override
        void init(Channel channel) throws Exception {
            final Map, Object> options = options0();
            synchronized (options) {
                setChannelOptions(channel, options, logger);
            }
    
            final Map, Object> attrs = attrs0();
            synchronized (attrs) {
                for (Entry, Object> e: attrs.entrySet()) {
                    @SuppressWarnings("unchecked")
                    AttributeKey key = (AttributeKey) e.getKey();
                    channel.attr(key).set(e.getValue());
                }
            }
    
            ChannelPipeline p = channel.pipeline();
    
            final EventLoopGroup currentChildGroup = childGroup;
            final ChannelHandler currentChildHandler = childHandler;
            final Entry, Object>[] currentChildOptions;
            final Entry, Object>[] currentChildAttrs;
            synchronized (childOptions) {
                currentChildOptions = childOptions.entrySet().toArray(newOptionArray(0));
            }
            synchronized (childAttrs) {
                currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0));
            }
    
            p.addLast(new ChannelInitializer() {
                @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));
                        }
                    });
                }
            });
        }
        
    }
     

    重点看最后的p.addLast()向serverChannel的流水线处理器中加入了一个 ServerBootstrapAcceptor,从名字上就可以看出来,这是一个接入器,专门接受新请求,把新的请求扔给某个事件循环器。

  • register channel:
    这个ChannelFuture regFuture = config().group().register(channel)实际调用的是
    NioEventLoop 中的register(实际是其父类SingleThreadEventLoop的对应方法):
    public abstract class SingleThreadEventLoop extends SingleThreadEventExecutor implements EventLoop {
        
        @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;
        }
        
    }

    这个promise.channel().unsafe().register(this, promise)实际是:AbstractUnsafe.register,后面代码没法理解了,后面再学吧

  •  

    服务端整体逻辑架构

                                                                      服务端 Netty Reactor 工作架构图

    Server 端包含 1 个 Boss NioEventLoopGroup 和 1 个 Worker NioEventLoopGroup。

    NioEventLoopGroup 相当于 1 个事件循环组,这个组里包含多个事件循环 NioEventLoop,每个 NioEventLoop 包含 1 个 Selector 和 1 个事件循环线程。

    每个 Boss NioEventLoop 循环执行的任务包含 3 步:

    • 轮询 Accept 事件。
    • 处理 Accept I/O 事件,与 Client 建立连接,生成 NioSocketChannel,并将 NioSocketChannel 注册到某个 Worker NioEventLoop 的 Selector 上。
    • 处理任务队列中的任务,runAllTasks。任务队列中的任务包括用户调用 eventloop.execute 或 schedule 执行的任务,或者其他线程提交到该 eventloop 的任务。

    每个 Worker NioEventLoop 循环执行的任务包含 3 步:

    • 轮询 Read、Write 事件。
    • 处理 I/O 事件,即 Read、Write 事件,在 NioSocketChannel 可读、可写事件发生时进行处理。
    • 处理任务队列中的任务,runAllTasks。

     

     

     

     

     

     

     

     

     

     

    参考文档:

    • Nioeventloop原理
    • netty源码分析之服务端启动全解析

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    你可能感兴趣的:(netty,netty)