Netty-新连接接入检测(源码分析)

新连接检测

在这里插入图片描述
NioEventLoop:

  /**
     * 以新连接接入分析处理过程为例
     * @param k
     * @param ch
     */
    private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {

        final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
        if (!k.isValid()) {
            final EventLoop eventLoop;
            try {
                eventLoop = ch.eventLoop();
            } catch (Throwable ignored) {
                // If the channel implementation throws an exception because there is no event loop, we ignore this
                // because we are only trying to determine if ch is registered to this event loop and thus has authority
                // to close ch.
                return;
            }
            // Only close ch if ch is still registered to this EventLoop. ch could have deregistered from the event loop
            // and thus the SelectionKey could be cancelled as part of the deregistration process, but the channel is
            // still healthy and should not be closed.
            // See https://github.com/netty/netty/issues/5125
            if (eventLoop == this) {
                // close the channel if the key is not valid anymore
                unsafe.close(unsafe.voidPromise());
            }
            return;
        }

        try {
            /**
             * 当检测到服务端的channel有事件发生。
             */
            int readyOps = k.readyOps();
            // We first need to call finishConnect() before try to trigger a read(...) or write(...) as otherwise
            // the NIO JDK channel implementation may throw a NotYetConnectedException.
            if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
                // remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking
                // See https://github.com/netty/netty/issues/924
                int ops = k.interestOps();
                ops &= ~SelectionKey.OP_CONNECT;
                k.interestOps(ops);

                unsafe.finishConnect();
            }

            // Process OP_WRITE first as we may be able to write some queued buffers and so free memory.
            if ((readyOps & SelectionKey.OP_WRITE) != 0) {
                // Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write
                ch.unsafe().forceFlush();
            }

            // Also check for readOps of 0 to workaround possible JDK bug which may otherwise lead
            // to a spin loop
            /**
             * 对应的连接事件发生,unsafe.read 实际上是AbstractNioMessageChannel.read的方法
             */
            if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
                unsafe.read();
            }
        } catch (CancelledKeyException ignored) {
            unsafe.close(unsafe.voidPromise());
        }
    }

AbstractNioMessageChannel

@Override
        /**
         * 调用具体的读事件处理逻辑
         */
        public void read() {
            assert eventLoop().inEventLoop();
            final ChannelConfig config = config();
            final ChannelPipeline pipeline = pipeline();
            /**
             * allocHandle用于控制客户端接入速率,每次最多读16个连接
             */
            final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
            allocHandle.reset(config);

            boolean closed = false;
            Throwable exception = null;
            try {
                try {
                    do {
                        /**
                         * readBuf是用于承载连接的容器
                         */
                        /**
                         * 读readMessage 如果返回的是0,代表没有连接可读,如果返回1代表读取了一个连接,
                         * 还要在进行doReadMessages(readBuf)调用来判断当前是不是还有连接未处理
                         */
                        int localRead = doReadMessages(readBuf);
                        if (localRead == 0) {
                            break;
                        }
                        if (localRead < 0) {
                            closed = true;
                            break;
                        }

                        allocHandle.incMessagesRead(localRead);
                    } while (continueReading(allocHandle));
                } catch (Throwable t) {
                    exception = t;
                }

                int size = readBuf.size();
                for (int i = 0; i < size; i ++) {
                    readPending = false;
                    pipeline.fireChannelRead(readBuf.get(i));
                }
                readBuf.clear();
                allocHandle.readComplete();
                pipeline.fireChannelReadComplete();

                if (exception != null) {
                    closed = closeOnReadError(exception);

                    pipeline.fireExceptionCaught(exception);
                }

                if (closed) {
                    inputShutdown = true;
                    if (isOpen()) {
                        close(voidPromise());
                    }
                }
            } finally {
                // Check if there is a readPending which was not processed yet.
                // This could be for two reasons:
                // * The user called Channel.read() or ChannelHandlerContext.read() in channelRead(...) method
                // * The user called Channel.read() or ChannelHandlerContext.read() in channelReadComplete(...) method
                //
                // See https://github.com/netty/netty/issues/2254
                if (!readPending && !config.isAutoRead()) {
                    removeReadOp();
                }
            }
        }
    }

NioServerSocketChannel
doReadMessages:

 @Override
    /**
     * 读取出一个新连接,即java底层的channel
     * 然后封装成netty的NioSocketChannel,添加到承载的容器中
     * 如果读取到连接,就返回1,否则返回0
     */
    protected int doReadMessages(List<Object> buf) throws Exception {
        SocketChannel ch = javaChannel().accept();
        try {
            if (ch != null) {
                 /**
                 * 将服务端channel与jdk底层的channel作为构造函数参数放进去。
                 */
                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;
    }

创建NioSocketChannel

在这里插入图片描述

创建NioSocketChannel时候主要做了两件事情,第一逐层调用父类方法,初始化id。unsafe,pipeline对象,
设置非阻塞模式,保存读事件。第二:创建NioSocketChannel配置类,禁用tcpnodelay算法。已达到快速发送的效果

protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
        super(parent, ch, SelectionKey.OP_READ);
    }

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

// 禁用tcpNoDelay
public DefaultSocketChannelConfig(SocketChannel channel, Socket javaSocket) {
        super(channel);
        this.javaSocket = ObjectUtil.checkNotNull(javaSocket, "javaSocket");
        // Enable TCP_NODELAY by default if possible.
        if (PlatformDependent.canEnableTcpNoDelayByDefault()) {
            try {
                setTcpNoDelay(true);
            } catch (Exception e) {
                // Ignore.
            }
        }
    }

channel分类
在这里插入图片描述

新连接分配线程和selector注册

在这里插入图片描述

回到现在刚拿到一条连接的过程。接着调用pipeline.fireChannelRead(新连接),将获取到的新连接通过
服务端的pipeline进行传递。在服务端channel初始化的时候如下图,设置了服务端的handler。
而这个hander,所做的事情就包括:
添加childhandler
设置options与attrs
选择NioEventloop并注册selector
在这里插入图片描述
pipeline.fireChannelRead(新连接)这个新连接就会传播到ServerBootstrapAcceptor的channelread方法中

   @Override
        public void read() {
            assert eventLoop().inEventLoop();
            final ChannelConfig config = config();
            final ChannelPipeline pipeline = pipeline();
            final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
            allocHandle.reset(config);
            boolean closed = false;
            Throwable exception = null;
            try {
                try {
                    do {
                        int localRead = doReadMessages(readBuf);
                        if (localRead == 0) {
                            break;
                        }
                        if (localRead < 0) {
                            closed = true;
                            break;
                        }
                        allocHandle.incMessagesRead(localRead);
                    } while (allocHandle.continueReading());
                } 
                int size = readBuf.size();
                for (int i = 0; i < size; i ++) {
                    readPending = false;
                    pipeline.fireChannelRead(readBuf.get(i));
                }
                readBuf.clear();
                allocHandle.readComplete();
                pipeline.fireChannelReadComplete();
            }
        }
    }

ServerBootstrapAcceptor.java

属性的由来:childGroup,childHandler,childOptions,childAttrs都是在服务端channel初始化的时候
在ServerBootstrap启动类中执行init方法,传递进去的,这些属性我是我们在启动类设置的。所以在下面
channelread方法中可以拿到。

ServerBootstrapAcceptor(
                final Channel channel, EventLoopGroup childGroup, ChannelHandler childHandler,
                Entry<ChannelOption<?>, Object>[] childOptions, Entry<AttributeKey<?>, Object>[] childAttrs) {
            this.childGroup = childGroup;
            this.childHandler = childHandler;
            this.childOptions = childOptions;
            this.childAttrs = childAttrs;
        }

 @Override
        @SuppressWarnings("unchecked")
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            final Channel child = (Channel) msg;
            // 添加我们自定义的chilhandler
            // 添加childhandler就是ChannelInitializer,触发ChannelInitializer的handlerAdded方法
            // 然后在handlerAdd方法中调用我们重写的init方法,进行handler添加,然后自己删除ChannelInitializer
            child.pipeline().addLast(childHandler);
            // 设置自定义tcp属性
            setChannelOptions(child, childOptions, logger);
            // 设置我们自定义的key
            for (Entry<AttributeKey<?>, Object> e: childAttrs) {
                child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
            }
            try {
            在这里就会选择一个nioeventloop并注册selector,见下面的注册分析
                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);
            }
        }

register:
@Override
    public ChannelFuture register(Channel channel) {
    // next是返回一个nioeventloop,在eventloop中执行方法。
        return next().register(channel);
    }
然后调用SingleThreadEventLoop的register方法
@Override
    public ChannelFuture register(Channel channel) {
        return register(new DefaultChannelPromise(channel, this));
    }
@Override
    public ChannelFuture register(final ChannelPromise promise) {
        ObjectUtil.checkNotNull(promise, "promise");
        promise.channel().unsafe().register(thisNioeventloop, promise);
        return promise;
    }

最终调用到AbstractChannel的register方法
接下来注册逻辑就和服务端一样了
从AbstractChannelregister()   
```java
@Override
        public final void register(EventLoop eventLoop, final ChannelPromise promise) {
            if (eventLoop == null) {
                throw new NullPointerException("eventLoop");
            }
            if (isRegistered()) {
                promise.setFailure(new IllegalStateException("registered to an event loop already"));
                return;
            }
            保存当前的eventloop
            AbstractChannel.this.eventLoop = eventLoop;
            if (eventLoop.inEventLoop()) {
                register0(promise);
            } else {
                try {
                    eventLoop.execute(new Runnable() {
                        @Override
                        public void run() {
                            register0(promise);
                        }
                    });
                } 
                }
            }
        }


最终调用AbstractNioChannel.java的doRegister方法
 @Override
    protected void doRegister() throws Exception {
        boolean selected = false;
        for (;;) {
            try {
                selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
                return;
            } catch (CancelledKeyException e) {
                if (!selected) {
                    // Force the Selector to select now as the "canceled" SelectionKey may still be
                    // cached and not removed because no Select.select(..) operation was called yet.
                    eventLoop().selectNow();
                    selected = true;
                } else {
                    // We forced a select operation on the selector before but the SelectionKey is still cached
                    // for whatever reason. JDK bug ?
                    throw e;
                }
            }
        }
    }

向selector注册读事件

register之后调用firechannelactive,进行事件传播,事件传播从pipeline的head头结点开始向下传播。在头结点的channelActive中会调用 readIfIsAutoRead();最后会传播到AbstractChannel的AbstractUnsafe的beginread方法,这个beginread方法最终会调用到AbstractNioChannel的dobeginread方法。然后向selector注册读事件。

 @Override
    protected void doBeginRead() throws Exception {
        // Channel.read() or ChannelHandlerContext.read() was called
		回到之前注册jdk底层channel的时候,返回了一个selectionkey,对于客户端channl注册的感兴趣事件为0,
		同时在创建客户端channel的时候,见下面代码,保存的readInterestOp为read事件
        final SelectionKey selectionKey = this.selectionKey;
        if (!selectionKey.isValid()) {
            return;
        }

        readPending = true;
        如果是客户端chnanel返回的是0
        final int interestOps = selectionKey.interestOps();
        所以下面的代码表示已经绑定成功了,告诉selector要关心一个read事件,selector发现如果有数据可读
        接入就回交由netty来处理
        if ((interestOps & readInterestOp) == 0) {
            selectionKey.interestOps(interestOps | readInterestOp);
        }
    }
```java
 public NioSocketChannel(ServerSocketChannel channel) {
        /**
         * SelectionKey.OP_ACCEPT:相当于注册感兴趣事件为读事件
         */
       super(parent, ch, SelectionKey.OP_READ);; ----super(parent, ch, readInterestOp);
        /**
         * 创建tcp参数配置类,比如backlog reuseaddr ,专属于单独的服务端或者客户端channel
         */
        config = new NioServerSocketChannelConfig(this, javaChannel().socket());
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值