美文网首页个人学习Netty
netty源码分析 - register分析

netty源码分析 - register分析

作者: 晴天哥_王志 | 来源:发表于2020-04-04 13:12 被阅读0次

    系列

    Netty源码分析 - Bootstrap服务端
    Netty源码分析 - Bootstrap客户端
    netty源码分析 - ChannelHandler
    netty源码分析 - EventLoop类关系
    netty源码分析 - register分析
    Netty源码分析 - NioEventLoop事件处理
    netty源码分析 - accept过程分析
    Netty源码分析 - ByteBuf
    Netty源码分析 - 粘包和拆包问题

    开篇

    • 这篇文章主要用于分析NioSocketChannel注册到NioEventLoop对象的过程,顺带和Nio的例子进行下对比。
    • 通过这篇文章能够了解到整个注册过程的脉络为NioEventLoopGroup => NioEventLoop => NioSocketChannel的刘哦吃。

    Nio的例子

    public class NioEchoServer {
        private static final int BUF_SIZE = 256;
        private static final int TIMEOUT = 3000;
    
        public static void main(String args[]) throws Exception {
            // 打开服务端 Socket,ServerSocketChannel.open()
            // 内部执行的是 SelectorProvider.provider().openServerSocketChannel();
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    
            // 打开 Selector
            Selector selector = Selector.open();
    
            // 服务端 Socket 监听8080端口, 并配置为非阻塞模式
            serverSocketChannel.socket().bind(new InetSocketAddress(8080));
            serverSocketChannel.configureBlocking(false);
    
            // 将 channel 注册到 selector 中.
            // 通常我们都是先注册一个 OP_ACCEPT 事件, 然后在 OP_ACCEPT 到来时, 再将这个 Channel 的 OP_READ
            // 注册到 Selector 中.
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    
            while (true) {
                // 通过调用 select 方法, 阻塞地等待 channel I/O 可操作
                if (selector.select(TIMEOUT) == 0) {
                    System.out.print(".");
                    continue;
                }
    
                // 获取 I/O 操作就绪的 SelectionKey, 通过 SelectionKey 可以知道哪些 Channel 的哪类 I/O 操作已经就绪.
                Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
    
                while (keyIterator.hasNext()) {
    
                    SelectionKey key = keyIterator.next();
    
                    // 当获取一个 SelectionKey 后, 就要将它删除, 表示我们已经对这个 IO 事件进行了处理.
                    keyIterator.remove();
    
                    if (key.isAcceptable()) {
                        // 当 OP_ACCEPT 事件到来时, 我们就有从 ServerSocketChannel 中获取一个 SocketChannel,
                        // 代表客户端的连接
                        // 注意, 在 OP_ACCEPT 事件中, 从 key.channel() 返回的 Channel 是 ServerSocketChannel.
                        // 而在 OP_WRITE 和 OP_READ 中, 从 key.channel() 返回的是 SocketChannel.
                        SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept();
                        clientChannel.configureBlocking(false);
                        //在 OP_ACCEPT 到来时, 再将这个 Channel 的 OP_READ 注册到 Selector 中.
                        // 注意, 这里我们如果没有设置 OP_READ 的话, 即 interest set 仍然是 OP_CONNECT 的话, 那么 select 方法会一直直接返回.
                        clientChannel.register(key.selector(), OP_READ, ByteBuffer.allocate(BUF_SIZE));
                    }
    
                    if (key.isReadable()) {
                        SocketChannel clientChannel = (SocketChannel) key.channel();
                        ByteBuffer buf = (ByteBuffer) key.attachment();
                        long bytesRead = clientChannel.read(buf);
                        if (bytesRead == -1) {
                            clientChannel.close();
                        } else if (bytesRead > 0) {
                            key.interestOps(OP_READ | SelectionKey.OP_WRITE);
                            System.out.println("Get data length: " + bytesRead);
                        }
                    }
    
                    if (key.isValid() && key.isWritable()) {
                        ByteBuffer buf = (ByteBuffer) key.attachment();
                        buf.flip();
                        SocketChannel clientChannel = (SocketChannel) key.channel();
    
                        clientChannel.write(buf);
    
                        if (!buf.hasRemaining()) {
                            key.interestOps(OP_READ);
                        }
                        buf.compact();
                    }
                }
            }
        }
    }
    

    Netty NioServer

    NioServerSocketChannel

    NioServerSocketChannel
    • NioServerSocketChannel的类继承关系如上图,核心关注NioServerSocketChannel和AbstraceNioChannel。
    • NioServerSocketChannel和AbstraceNioChannel类关注初始化顺序。
    public class NioServerSocketChannel extends AbstractNioMessageChannel
                                 implements io.netty.channel.socket.ServerSocketChannel {
    
        private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16);
        // SelectorProvider.provider()返回的是单例对象
        private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
        private final ServerSocketChannelConfig config;
    
        private static ServerSocketChannel newSocket(SelectorProvider provider) {
            try {
                return provider.openServerSocketChannel();
            } catch (IOException e) {
            }
        }
    
        public NioServerSocketChannel() {
            this(newSocket(DEFAULT_SELECTOR_PROVIDER));
        }
    
        public NioServerSocketChannel(ServerSocketChannel channel) {
            super(null, channel, SelectionKey.OP_ACCEPT);
            config = new NioServerSocketChannelConfig(this, javaChannel().socket());
        }
    }
     
    
    
    public abstract class AbstractNioChannel extends AbstractChannel {
    
        private final SelectableChannel ch;
        protected final int readInterestOp;
        volatile SelectionKey selectionKey;
    
        protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
            super(parent);
            this.ch = ch;
            this.readInterestOp = readInterestOp;
            try {
                // 如果一个Channel要注册到Selector中, 那么这个Channel必须是非阻塞的
                // 即channel.configureBlocking(false);
                ch.configureBlocking(false);
            } catch (IOException e) {
            }
        }
    
        protected SelectableChannel javaChannel() {
            return ch;
        }
    }
    
    • NioServerSocketChannel核心关注SelectableChannel和readInterestOp。
    • DEFAULT_SELECTOR_PROVIDER是通过SelectorProvider.provider()返回单例对象。
    • SelectableChannel对象由provider.openServerSocketChannel()。
    • readInterestOp在NioServerSocketChannel中监听事件为SelectionKey.OP_ACCEPT。
    • 核心关注ServerSocketChannel,继续关注分析。
    • 核心关注SelectorProvider,用于创建ServerSocketChannel对象,继续关注分析。

    ServerSocketChannel

    ServerSocketChannel
    • ServerSocketChannel的类关系图如上图所示,突出父类SelectableChannel,解答AbstractNioChannel的参数对象SelectableChannel的缘由。

    SelectorProvider

    SelectorProvider
    • SelectorProvider的类图如上所示,生成ServerSocketChannel对象,分析下原理。
    public abstract class SelectorProvider {
    
        private static final Object lock = new Object();
        private static SelectorProvider provider = null;
    
        public static SelectorProvider provider() {
            synchronized (lock) {
                if (provider != null)
                    return provider;
                return AccessController.doPrivileged(
                    new PrivilegedAction<SelectorProvider>() {
                        public SelectorProvider run() {
                                if (loadProviderFromProperty())
                                    return provider;
                                if (loadProviderAsService())
                                    return provider;
                                provider = sun.nio.ch.DefaultSelectorProvider.create();
                                return provider;
                            }
                        });
            }
        }
    }
    
    
    public abstract class SelectorProviderImpl extends SelectorProvider {
    
        public abstract AbstractSelector openSelector() throws IOException;
    
        public ServerSocketChannel openServerSocketChannel() throws IOException {
            return new ServerSocketChannelImpl(this);
        }
    
        public SocketChannel openSocketChannel() throws IOException {
            return new SocketChannelImpl(this);
        }
    }
    
    • SelectorProvider#provider用于生成单例的SelectorProvider对象。
    • SelectorProviderImpl#openServerSocketChannel负责创建ServerSocketChannel对象。
    • SelectorProviderImpl#openSocketChannel负责创建SocketChannel对象。
    • 在Netty的体系里面channel对象都是由SelectorProvider来完成的,相当于灵魂。

    register分析

    注册调用链
    • channel注册调用链如上图,按照NioEventLoopGroup => NioEventLoop => NioSocketChannel进行注册。
    • 最终的注册是将NioSocketChannel注册到NioEventLoop的selector对象当中。

    NioEventLoopGroup注册

    public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
    
        final ChannelFuture initAndRegister() {
            Channel channel = null;
            try {
                channel = channelFactory.newChannel();
                init(channel);
            } catch (Throwable t) {
            }
            // 进入NioEventLoopGroup的register过程,进入NioEventLoop的过程
            ChannelFuture regFuture = config().group().register(channel);
            if (regFuture.cause() != null) {
                if (channel.isRegistered()) {
                    channel.close();
                } else {
                    channel.unsafe().closeForcibly();
                }
            }
    
            return regFuture;
        }
    }
    
    public abstract class MultithreadEventLoopGroup extends MultithreadEventExecutorGroup implements EventLoopGroup {
    
        public ChannelFuture register(Channel channel) {
            return next().register(channel);
        }
    }
    
    • config().group().register(channel)的方法中从NioEventLoopGroup执行register方法。
    • next().register(channel)的方法实现从NioEventLoopGroup到NioEventLoop的调用传递。

    NioEventLoop注册

    public abstract class SingleThreadEventLoop extends SingleThreadEventExecutor implements EventLoop {
    
        public ChannelFuture register(Channel channel) {
            // DefaultChannelPromise(channel, this)的this为NioEventLoop对象
            return register(new DefaultChannelPromise(channel, this));
        }
    
        public ChannelFuture register(final ChannelPromise promise) {
    
            ObjectUtil.checkNotNull(promise, "promise");
            // 由NioEventLoop进入NioSocketChannel的register过程
            promise.channel().unsafe().register(this, promise);
            return promise;
        }
    }
    
    • promise.channel().unsafe().register(this, promise)实现由NioEventLoop到NioSocketChannel的调用传递。

    NioSocketChannel注册

    public abstract class AbstractNioChannel extends AbstractChannel {
    
        protected abstract class AbstractUnsafe implements Unsafe {
            public final void register(EventLoop eventLoop, final ChannelPromise promise) {
                AbstractChannel.this.eventLoop = eventLoop;
    
                if (eventLoop.inEventLoop()) {
                    register0(promise);
                } else {
                    try {
                        eventLoop.execute(new Runnable() {
                            @Override
                            public void run() {
                                register0(promise);
                            }
                        });
                    } catch (Throwable t) {
                    }
                }
            }
    
            private void register0(ChannelPromise promise) {
                try {
                    // 省略代码
                    doRegister();
                } catch (Throwable t) {
                }
            }
    
        }
    
        protected void doRegister() throws Exception {
            boolean selected = false;
            for (;;) {
                try {
                    // 绑定NioSocketChannel到NioEventLoop对象当中,并且附加NioSocketChannel对象。
                    selectionKey = javaChannel().register(eventLoop().selector, 0, this);
                    return;
                } catch (CancelledKeyException e) {
                }
            }
        }
    }
    
    • AbstractUnsafe#register执行的NioSocketChannel的注册动作,最终会执行AbstractNioChannel#doRegister。
    • AbstractNioChannel#doRegister内部通过javaChannel().register(eventLoop().selector, 0, this)实现绑定Channel到NioEventLoop对象当中。

    相关文章

      网友评论

        本文标题:netty源码分析 - register分析

        本文链接:https://www.haomeiwen.com/subject/jbfdphtx.html