美文网首页
Netty学习笔记(九)新连接接入

Netty学习笔记(九)新连接接入

作者: 云师兄 | 来源:发表于2018-11-17 17:36 被阅读11次

    引子

    在上一篇分析NioEventLoop那篇文章中分析NioEventLoop.run()时候提到当方法processSelectedKey()处理事件的时候,比如读事件,最终调用了unsafe.read();本文就来分析一下unsafe对象的read方法的具体实现。

    检测新的连接

    源码中的Unsafe是一个接口,在调试时可知其实现类为NioMessageUnsafe类,其read方法源码如下:

            private final List<Object> readBuf = new ArrayList<Object>();
            final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
            @Override
            public void read() { 
            ......
                    try {
                        do {
                            int localRead = doReadMessages(readBuf);
                            if (localRead == 0) {
                                break;
                            }
                            if (localRead < 0) {
                                closed = true;
                                break;
                            }
                            allocHandle.incMessagesRead(localRead);
                        } while (allocHandle.continueReading());
                    }
            ......
    

    doReadMessages()方法在当前类是一个抽闲方法,实现在NioServerSocketChannel类中,如下:

        protected int doReadMessages(List<Object> buf) throws Exception {
            SocketChannel ch = javaChannel().accept();
            try {
                if (ch != null) {
                    buf.add(new NioSocketChannel(this, ch));
                    return 1;
                }
            } catch (Throwable t) {
                try {
                    ch.close();
                } catch (Throwable t2) {
                    logger.warn("Failed to close a socket.", t2);
                }
            }
            return 0;
        }
    

    可以看出当一个新的read事件过来的时候,服务端accept后创建SocketChannel对象,并封装为NioSocketChannel对象,放到一个链表中。
    此外,上述read方法是一个循环,条件是allocHandle.continueReading()值为true,实际运行中,这个方法实现如下:

            public boolean continueReading() {
                return config.isAutoRead() &&
                       attemptedBytesRead == lastBytesRead &&
                       totalMessages < maxMessagePerRead &&
                       totalBytesRead < Integer.MAX_VALUE;
            }
    

    这个方法的意思是当配置配了自动读,并且一次进去总共的连接数不能超过maxMessagePerRead(默认值为16)个连接。如果下一次执行循环的时候没有连接的话,那么while循环就结束了。
    分析到这里,检测新连接的意思就是:获取当前读取操作有几个连接进来,封装成NioSocketChannel对象并放在一个readBuf链表里面。

    NioSocketChannel的创建

    在讲述检测连接的地方提到将SocketChannel封装为NioSocketChannel对象并进行创建,这里接着分析这个Netty提供的NioSocketChannel的实例化过程:

        public NioSocketChannel(Channel parent, SocketChannel socket) {
            super(parent, socket);
            config = new NioSocketChannelConfig(this, socket.socket());
        }
    

    首先调用了父类的构造函数,实现为:

        protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
            super(parent);
            this.ch = ch;
            this.readInterestOp = readInterestOp;
            try {
                ch.configureBlocking(false);
            } catch (IOException e) {
                try {
                    ch.close();
                } catch (IOException e2) {
                    if (logger.isWarnEnabled()) {
                        logger.warn(
                                "Failed to close a partially initialized socket.", e2);
                    }
                }
                throw new ChannelException("Failed to enter non-blocking mode.", e);
            }
        }
    

    其中传入readInterestOp = SelectionKey.OP_READ即感兴趣的事件为读事件。在构造函数中主要是设置了SocketChannel的阻塞模式为非阻塞,这是NIO编程中实现选择器进行选择的关键设置。
    我们接着看进一步的父类构造函数调用:

        protected AbstractChannel(Channel parent) {
            this.parent = parent;
            id = newId();
            unsafe = newUnsafe();
            pipeline = newChannelPipeline();
        }
    

    所以总结来说NioSocketChannel里面有几个属性:

    • parent(保存了NioServerSocketChannel对象)。
    • id
    • unsafe
    • pipeline

    关于unsafe和pipeline,后续将会对其进行分析理解。我们接着回过头来看NioSocketChannel构造函数的第二个语句:config = new NioSocketChannelConfig(this, socket.socket());,即创建一个配置类NioSocketChannelConfig,这个配置类最终构造函数实现:

        public DefaultSocketChannelConfig(SocketChannel channel, Socket javaSocket) {
            super(channel);
            if (javaSocket == null) {
                throw new NullPointerException("javaSocket");
            }
            this.javaSocket = javaSocket;
    
            // Enable TCP_NODELAY by default if possible.
            if (PlatformDependent.canEnableTcpNoDelayByDefault()) {
                try {
                    setTcpNoDelay(true);
                } catch (Exception e) {
                    // Ignore.
                }
            }
        }
        public SocketChannelConfig setTcpNoDelay(boolean tcpNoDelay) {
            try {
                javaSocket.setTcpNoDelay(tcpNoDelay);
            } catch (SocketException e) {
                throw new ChannelException(e);
            }
            return this;
        }
    

    可以看出配置类主要初始化的时候就配置了一个东西:调用Socket对象的setTcpNoDelay(true)方法。
    关于java Socket中这个配置具体起到什么作用,不清楚,看源码备注:disable/enable Nagle's algorithm是用来配置是否启动一个Nagle算法加的,这里先知道干了这么一件事就行了。

    相关文章

      网友评论

          本文标题:Netty学习笔记(九)新连接接入

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