Netty Channel ---AbstractNioByteChannel & AbstractNioMessageChannel源碼解析

AbstractNioByteChannel源碼解析    
@Override protected void doWrite(ChannelOutboundBuffer in) throws Exception { int writeSpinCount = -1; for (;;) {         // 從當前的消息發送環形數組中彈出一條消息 Object msg = in.current();         // 消息爲null,說明所有消息已經發送完成,清除寫操作位 if (msg == null) { // Wrote all messages. clearOpWrite(); break; }         // 如果msg是ByteBuf類型 if (msg instanceof ByteBuf) { ByteBuf buf = (ByteBuf) msg; int readableBytes = buf.readableBytes();             // 如果消息沒有可讀字節,則丟棄消息,進行下一次循環 if (readableBytes == 0) { in.remove(); continue; }             // 設置寫半包標識 boolean setOpWrite = false;             // 設置消息是否全部發送標誌 boolean done = false;             // 設置發送的消息總字節數 long flushedAmount = 0;             // 獲取循環發送次數:是爲了當一次發送沒有完成時,繼續循環發送的次數,設置寫半包的最大循環次數的原因是循環發送的時候,             // IO線程會一直進行寫操作, 此時IO線程無法處理其他的IO操作,例如讀取新的消息或者執行定時任務等;如果網絡IO阻塞或者對方接受消息太慢,             // 可能導致線程假死 if (writeSpinCount == -1) { writeSpinCount = config().getWriteSpinCount(); } for (int i = writeSpinCount - 1; i >= 0; i --) { int localFlushedAmount = doWriteBytes(buf); if (localFlushedAmount == 0) {                     // 如果本次發送的字節數爲0,說明TCP發送緩衝區已滿,發生了ZERO_WINDOW, 此時再次發送可能出現寫0字節,空循環會佔用CPU資源                     // 所以將寫半包標識設置爲true,退出循環。釋放IO線程。 setOpWrite = true; break; } flushedAmount += localFlushedAmount;                 // 消息沒有可讀字節,說明消息已經發送成功,退出本次循環; if (!buf.isReadable()) { done = true; break; } } in.progress(flushedAmount);             // 如果消息發送彎沉,將消息從消息發送循環數組中移除 if (done) { in.remove(); } else {                 // 設置寫半包標識,啓動刷新線程繼續發送之前沒有發送完成的半包消息--寫半包 incompleteWrite(setOpWrite); break; }         // 分支二 文件傳輸 } else if (msg instanceof FileRegion) { FileRegion region = (FileRegion) msg; boolean done = region.transfered() >= region.count(); boolean setOpWrite = false; if (!done) { long flushedAmount = 0; if (writeSpinCount == -1) { writeSpinCount = config().getWriteSpinCount(); } for (int i = writeSpinCount - 1; i >= 0; i--) { long localFlushedAmount = doWriteFileRegion(region); if (localFlushedAmount == 0) { setOpWrite = true; break; } flushedAmount += localFlushedAmount; if (region.transfered() >= region.count()) { done = true; break; } } in.progress(flushedAmount); } if (done) { in.remove(); } else { incompleteWrite(setOpWrite); break; } } else { // Should not reach here. throw new Error(); } } }
protected final void incompleteWrite(boolean setOpWrite) {
        // Did not write completely.
        // 判斷並設置寫半包
        if (setOpWrite) {
            setOpWrite();
        } else {
            // Schedule flush again later so other tasks can be picked up in the meantime
            // 啓動刷新線程繼續寫半包
            Runnable flushTask = this.flushTask;
            if (flushTask == null) {
                flushTask = this.flushTask = new Runnable() {
                    @Override
                    public void run() {
                        flush();
                    }
                };
            }
            eventLoop().execute(flushTask);
        }
    }
    // AbstractNioMessageChannel 與AbstractNioByteChannel的不同是: 
  // 前者發送的是POJO對象,後者發送的是ByteBuf或者FileRegion
    @Override
    protected void doWrite(ChannelOutboundBuffer in) throws Exception {
        final SelectionKey key = selectionKey();
        final int interestOps = key.interestOps();

        for (;;) {
            Object msg = in.current();
            if (msg == null) {
                // Wrote all messages.
                if ((interestOps & SelectionKey.OP_WRITE) != 0) {
                    key.interestOps(interestOps & ~SelectionKey.OP_WRITE);
                }
                break;
            }
            try {
                boolean done = false;
                for (int i = config().getWriteSpinCount() - 1; i >= 0; i--) {
                    if (doWriteMessage(msg, in)) {
                        done = true;
                        break;
                    }
                }

                if (done) {
                    in.remove();
                } else {
                    // Did not write all messages.
                    if ((interestOps & SelectionKey.OP_WRITE) == 0) {
                        key.interestOps(interestOps | SelectionKey.OP_WRITE);
                    }
                    break;
                }
            } catch (IOException e) {
                if (continueOnWriteError()) {
                    in.remove(e);
                } else {
                    throw e;
                }
            }
        }
    }

NioServerSocketChannel:

public class NioServerSocketChannel extends AbstractNioMessageChannel
                             implements io.netty.channel.socket.ServerSocketChannel {
    
    // 
    private static final ChannelMetadata METADATA = new ChannelMetadata(false);
    private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();

    private static final InternalLogger logger = InternalLoggerFactory.getInstance(NioServerSocketChannel.class);
    
    // 打開新的ServerSocketChannel通道
    private static ServerSocketChannel newSocket(SelectorProvider provider) {
        try {
            /**
             *  Use the {@link SelectorProvider} to open {@link SocketChannel} and so remove condition in
             *  {@link SelectorProvider#provider()} which is called by each ServerSocketChannel.open() otherwise.
             *
             *  See <a href="See https://github.com/netty/netty/issues/2308">#2308</a>.
             */
            return provider.openServerSocketChannel();
        } catch (IOException e) {
            throw new ChannelException(
                    "Failed to open a server socket.", e);
        }
    }
    
    // 配置Channel的TCP參數
    private final ServerSocketChannelConfig config;
   @Override
    protected int doReadMessages(List<Object> buf) throws Exception {
        SocketChannel ch = javaChannel().accept();

        try {
            if (ch != null) {
                // 讀取操作就是:接受客戶端鏈接,創建NioSocketChannel對象
                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:

    // 鏈接的三種狀態:
    // 1.鏈接成功,返回true
    // 2.暫時沒有鏈接上,服務端沒有返回ACK應答,鏈接結果不確定,返回false;
    // 3.鏈接失敗,拋出異常
    @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);
            // 如果未鏈接上,設置selectionKey未OP_CONNECT,監聽鏈接網絡操作位;
            if (!connected) {
                selectionKey().interestOps(SelectionKey.OP_CONNECT);
            }
            success = true;
            return connected;
        } finally {
            if (!success) {
                // 如果拋出異常,則關閉客戶端鏈接;
                doClose();
            }
        }
    }

寫半包:

   @Override
    protected void doWrite(ChannelOutboundBuffer in) throws Exception {
        for (;;) {
            // 獲取待發送的ByteBuf個數;
            int size = in.size();
            if (size == 0) {
                // All written so clear OP_WRITE
                // clear OP_WRITE 
                clearOpWrite();
                break;
            }
            // 已經寫出去的字節數
            long writtenBytes = 0;
            // 消息發送完成標識
            boolean done = false;
            // 是否設置寫半包標識
            boolean setOpWrite = false;

            // Ensure the pending writes are made of ByteBufs only.
            ByteBuffer[] nioBuffers = in.nioBuffers();
            // 要發送的ByteBufer的數量
            int nioBufferCnt = in.nioBufferCount();
            // 需要發送的總字節數
            long expectedWrittenBytes = in.nioBufferSize();
            SocketChannel ch = javaChannel();

            // Always us nioBuffers() to workaround data-corruption.
            // See https://github.com/netty/netty/issues/2761
            switch (nioBufferCnt) {
                case 0:
                    // We have something else beside ByteBuffers to write so fallback to normal writes.
                    super.doWrite(in);
                    return;
                case 1:
                    // Only one ByteBuf so use non-gathering write
                    ByteBuffer nioBuffer = nioBuffers[0];
                    for (int i = config().getWriteSpinCount() - 1; i >= 0; i --) {
                        final int localWrittenBytes = ch.write(nioBuffer);
                        if (localWrittenBytes == 0) {
                            setOpWrite = true;
                            break;
                        }
                        expectedWrittenBytes -= localWrittenBytes;
                        writtenBytes += localWrittenBytes;
                        if (expectedWrittenBytes == 0) {
                            done = true;
                            break;
                        }
                    }
                    break;
                default:
                    // 寫操作次數上限進行控制,因爲如果發送緩衝區滿,TCP處於KEEP-ALIVE狀態,消息無法發送出去,線程無法執行其他任務
                    for (int i = config().getWriteSpinCount() - 1; i >= 0; i --) {
                        // 寫入SocketChannel的字節數
                        final long localWrittenBytes = ch.write(nioBuffers, 0, nioBufferCnt);
                        // 緩衝區滿,設置寫半包標識未true,用於向多路複用器註冊OP_WRITE
                        if (localWrittenBytes == 0) {
                            setOpWrite = true;
                            break;
                        }
                        // 需要發送的字節數
                        expectedWrittenBytes -= localWrittenBytes;
                        // 寫出去的字節數
                        writtenBytes += localWrittenBytes;
                        // 發送完成
                        if (expectedWrittenBytes == 0) {
                            done = true;
                            break;
                        }
                    }
                    break;
            }

            // Release the fully written buffers, and update the indexes of the partially written buffer.
            // 移除寫出去的字節數
            in.removeBytes(writtenBytes);
            
            // 寫半包
            if (!done) {
                // Did not write all buffers completely.
                incompleteWrite(setOpWrite);
                break;
            }
        }
    }
  protected final void incompleteWrite(boolean setOpWrite) {
        // Did not write completely.
        if (setOpWrite) {
            setOpWrite();
        } else {
            // Schedule flush again later so other tasks can be picked up in the meantime
            Runnable flushTask = this.flushTask;
            if (flushTask == null) {
                flushTask = this.flushTask = new Runnable() {
                    @Override
                    public void run() {
                        flush();
                    }
                };
            }
            eventLoop().execute(flushTask);
        }
    }

讀寫操作:實際上就是從SocketChannel中讀取L個字節到ByteBuf中,L爲ByteBuf可寫的字節數。

   @Override
    protected int doReadBytes(ByteBuf byteBuf) throws Exception {
        return byteBuf.writeBytes(javaChannel(), byteBuf.writableBytes());
    }
 // AbstractByteBuf  
 @Override
    public int writeBytes(ScatteringByteChannel in, int length) throws IOException {
        ensureAccessible();
        ensureWritable(length);
        int writtenBytes = setBytes(writerIndex, in, length);
        if (writtenBytes > 0) {
            writerIndex += writtenBytes;
        }
        return writtenBytes;
    }
    @Override
    public int setBytes(int index, ScatteringByteChannel in, int length) throws IOException {
        ensureAccessible();
        try {
            // 從SocketChannel中讀取字節數組到緩衝區ByteBuffer中, 它的其實position爲writeIndex, limit爲writeIndex + length;
            return in.read((ByteBuffer) internalNioBuffer().clear().position(index).limit(index + length));
        } catch (ClosedChannelException ignored) {
            return -1;
        }
    }








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章