写自己的netty源码分析--新连接接入

绪论

两个问题

  1. netty是在哪里检测到有新连接接入的?
  2. 新连接是怎样注册到NioEventLoop线程的?

处理逻辑

  1. 检测新连接
  2. 创建NioSocketChannel
  3. 分配线程及注册selector
  4. 向selector注册读事件

检测新连接

processSelectedKey(key, channel) [入口]

  1. NioMessageUnsafe.read()
  2. doReadMessages()
  3. javaChannel().accept()
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
        final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
        ......

        try {
            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
            if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
                unsafe.read();
            }
        } catch (CancelledKeyException ignored) {
            unsafe.close(unsafe.voidPromise());
        }
    }

进入AbstractNioMessageChannel.NioMessageUnsafe.read()方法

public void read() {
            assert eventLoop().inEventLoop();  //确保线程在NioEventLoop方法内调用
            final ChannelConfig config = 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); //readBuf--存取临时读到的连接
                        if (localRead == 0) {
                            break;
                        }
                        if (localRead < 0) {
                            closed = true;
                            break;
                        }

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

进入doReadMessages()方法

protected int doReadMessages(List<Object> buf) throws Exception {
        SocketChannel ch = SocketUtils.accept(javaChannel());  //获取服务端启动过程中创建的JDK底层channel

        try {
            if (ch != null) {
                buf.add(new NioSocketChannel(this, ch));  //把词channel封装成NioSocketChannel放进buf中
                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;
    }

进入continueReading()方法

public boolean continueReading(UncheckedBooleanSupplier maybeMoreDataSupplier) {
    return config.isAutoRead() &&
           (!respectMaybeMoreData || maybeMoreDataSupplier.get()) &&
           totalMessages < maxMessagePerRead &&
           totalBytesRead > 0;
}

totalMessages -- 读到的连接
maxMessagePerRead -- 每次可以读到的最大连接,默认为16

创建NioSocketChannel

进入方式

processSelectedKey(key, channel)-->NioMessageUnsafe.read()-->doReadMessages()-->new NioSocketChannel(this, ch)

其中,this指NioServerSocketChannel,ch指客户端channel。进入NioSocketChannel()方法:

public NioSocketChannel(Channel parent, SocketChannel socket) {
        super(parent, socket);
        config = new NioSocketChannelConfig(this, socket.socket());
    }
这里可以分为两个分支。
首先
进入super()方法:
protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
        super(parent, ch, SelectionKey.OP_READ); //若有事件读写,会发出通知
    }
继续进入super()方法:
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent);
        this.ch = ch;  //保存客户端channel
        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);
        }
    }
继续进入super()方法:
protected AbstractChannel(Channel parent) {
        this.parent = parent;
        id = newId();
        unsafe = newUnsafe();
        pipeline = newChannelPipeline();
    }
其次,第二个分支
进入NioSocketChannelConfig()方法:
public DefaultSocketChannelConfig(SocketChannel channel, Socket javaSocket) {
        super(channel);
        if (javaSocket == null) {
            throw new NullPointerException("javaSocket");
        }
        this.javaSocket = javaSocket;  //保存socket

        // Enable TCP_NODELAY by default if possible.
        if (PlatformDependent.canEnableTcpNoDelayByDefault()) { //看是否支持安卓,不是返回true
            try {
                setTcpNoDelay(true);
            } catch (Exception e) {
                // Ignore.
            }
        }
    }
进入setTcpNoDelay(true)方法:
public SocketChannelConfig setTcpNoDelay(boolean tcpNoDelay) {
        try {
            javaSocket.setTcpNoDelay(tcpNoDelay);
        } catch (SocketException e) {
            throw new ChannelException(e);
        }
        return this;
    }
public void setTcpNoDelay(boolean on) throws SocketException { //进制Nagle算法,小的数据包发出去会降低延迟
        if (isClosed())
            throw new SocketException("Socket is closed");
        getImpl().setOption(SocketOptions.TCP_NODELAY, Boolean.valueOf(on));
    }

新连接NioEventLoop分配及注册selector

在服务端启动过程中,会专门添加一个channelHandler,专门处理新连接接入逻辑。在服务端启动的过程中,init()方法中会把服务端channel传入进来,然后初始化该channel。在接下来的代码中,会默认添加ServerBootstrapAcceptor()连接处理器,将其他用户自定义属性传进去。服务端启动init()方法

ServerBootstrapAcceptor

服务端Channel的pipeline构成

Head --> ServerBootstrapAcceptor --> Tail

客户端的每一条连接都会经历上述过程传播。

 private final class NioMessageUnsafe extends AbstractNioUnsafe {

        private final List<Object> readBuf = new ArrayList<Object>();

        @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);  //已经创建了客户端channel
                        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();
                for (int i = 0; i < size; i ++) {
                    readPending = false;
                //对客户端每一条连接,调用服务端的pipeline中的fireChannelRead方法将客户端的
                //连接传入到ServerBootstrapAcceptor()
                    pipeline.fireChannelRead(readBuf.get(i)); 
                }

ServerBootstrapAcceptor()主要做以下事情:

  1.  添加childerHandler;
  2. 设置options和attrs;
  3. 选择NioEventLoop并注册selector;

添加childerHandler

此childerHandler实际是ChannelInitializer,里面会有一个initChannel()方法,可以拿到pipeline,让用户自定义handler。

进入ChannelInitializer类中的方法:
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        if (ctx.channel().isRegistered()) {
            // This should always be true with our current DefaultChannelPipeline implementation.
            // The good thing about calling initChannel(...) in handlerAdded(...) is that there will be no ordering
            // surprises if a ChannelInitializer will add another ChannelInitializer. This is as all handlers
            // will be added in the expected order.
            if (initChannel(ctx)) {

                // We are done with init the Channel, removing the initializer now.
                removeState(ctx);
            }
        }
    }

    @SuppressWarnings("unchecked")
    private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
        if (initMap.add(ctx)) { // Guard against re-entrance.
            try {
                initChannel((C) ctx.channel()); //首先回调到起始位置,添加handler
            } catch (Throwable cause) {
                // Explicitly call exceptionCaught(...) as we removed the handler before calling initChannel(...).
                // We do so to prevent multiple calls to initChannel(...).
                exceptionCaught(ctx, cause);
            } finally {
                ChannelPipeline pipeline = ctx.pipeline();
                if (pipeline.context(this) != null) {
                    pipeline.remove(this); //最终将自身remove
                }
            }
            return true;
        }
        return false;
    }

设置options和attrs

 

向selector注册读事件

 

 

 

 

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值