Netty源碼分析-NioByteUnsafe(read讀取流程)

NioByteUnsafe封裝了NioSocketChannel讀取底層數據的流程。

NioEventLoop負責監聽Selector上所有的事件,當發生事件時根據事件類型調用Channel的UnSafe中的方法去處理。

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

 

NioByteUnsafe當中的read方法

1、分配ByteBuf。

2、從底層SocketChannel中讀取字節,封裝到ByteBuf中。

3、調用Channel的PPLine交給管道中的編解碼器去處理。

4、調用各種事件。

 @Override
        public final void read() {
            final ChannelConfig config = config();
            if (shouldBreakReadReady(config)) {
                clearReadPending();
                return;
            }
            //每個channel對應一個PPLine
            final ChannelPipeline pipeline = pipeline();
            //ByteBuf分配器
            final ByteBufAllocator allocator = config.getAllocator();
            //容量計算器
            final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle();
            //重置,把之前計數的值全部清空
            allocHandle.reset(config);

            ByteBuf byteBuf = null;
            boolean close = false;
            try {
                do {
                    //分配內存,關鍵在於計算分配內存的大小(小了不夠,大了浪費)
                    byteBuf = allocHandle.allocate(allocator);
                    //doReadBytes,從socket讀取字節到byteBuf,返回真實讀取數量
                    //更新容量計算器
                    allocHandle.lastBytesRead(doReadBytes(byteBuf));
                    //如果小於0 則socket關閉,如果等於0則沒讀取到數據
                    if (allocHandle.lastBytesRead() <= 0) {
                        // nothing was read. release the buffer.
                        //釋放資源
                        byteBuf.release();
                        byteBuf = null;
                        //如果小於0則意味着socket關閉
                        close = allocHandle.lastBytesRead() < 0;
                        if (close) {
                            // There is nothing left to read as we received an EOF.
                            readPending = false;
                        }
                        break;
                    }

                    //增加循環計數器
                    allocHandle.incMessagesRead(1);
                    readPending = false;
                    //把讀取到的數據,交給管道去處理
                    pipeline.fireChannelRead(byteBuf);
                    byteBuf = null;
                    //判斷是否繼續從socket讀取數據
                } while (allocHandle.continueReading());

                //讀取完成後調用readComplete,重新估算內存分配容量
                allocHandle.readComplete();
                //事件激發
                pipeline.fireChannelReadComplete();

                //如果需要關閉,則處理關閉
                if (close) {
                    closeOnRead(pipeline);
                }
            } catch (Throwable t) {
                handleReadException(pipeline, byteBuf, t, close, allocHandle);
            } 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
                //
                //根據情況移除OP_READ事件
                if (!readPending && !config.isAutoRead()) {
                    removeReadOp();
                }
            }
        }

在讀取中遇到異常,或者返回EOF(-1),說明底層通道關閉,需要處理關閉邏輯。

 private void closeOnRead(ChannelPipeline pipeline) {
            //底層輸入流未關閉
            if (!isInputShutdown0()) {
                //是否允許辦關閉
                if (isAllowHalfClosure(config())) {
                    //關閉輸入流
                    shutdownInput();
                    pipeline.fireUserEventTriggered(ChannelInputShutdownEvent.INSTANCE);
                } else {
                    //關閉socket
                    close(voidPromise());
                }
            } else {
                //激發事件
                inputClosedSeenErrorOnRead = true;
                pipeline.fireUserEventTriggered(ChannelInputShutdownReadComplete.INSTANCE);
            }
        }

異常處理的邏輯

        //異常處理
        private void handleReadException(ChannelPipeline pipeline, ByteBuf byteBuf, Throwable cause, boolean close,
                RecvByteBufAllocator.Handle allocHandle) {
            if (byteBuf != null) {
                //如果byteBuf有數據,則交給管道處理
                if (byteBuf.isReadable()) {
                    readPending = false;
                    pipeline.fireChannelRead(byteBuf);
                } else {
                    //釋放資源
                    byteBuf.release();
                }
            }
            //容量計算機更新guess大小
            allocHandle.readComplete();
            //激發讀完成事件和異常事件
            pipeline.fireChannelReadComplete();
            pipeline.fireExceptionCaught(cause);

            //關閉socket
            if (close || cause instanceof IOException) {
                closeOnRead(pipeline);
            }
        }

 

關閉socket的邏輯,需要釋放輸出隊列緩存,觸發關閉事件,關閉底層socket等。

 private void close(final ChannelPromise promise, final Throwable cause,
                           final ClosedChannelException closeCause, final boolean notify) {
            //設置不能取消
            if (!promise.setUncancellable()) {
                return;
            }

            //防止二次關閉,如果已經關閉
            if (closeInitiated) {
                if (closeFuture.isDone()) {
                    // Closed already.
                    safeSetSuccess(promise);
                } else if (!(promise instanceof VoidChannelPromise)) { // Only needed if no VoidChannelPromise.
                    // This means close() was called before so we just register a listener and return
                    closeFuture.addListener(new ChannelFutureListener() {
                        @Override
                        public void operationComplete(ChannelFuture future) throws Exception {
                            promise.setSuccess();
                        }
                    });
                }
                return;
            }

            closeInitiated = true;
            //判斷當前連接是否還連着
            final boolean wasActive = isActive();
            //輸出緩存隊列
            final ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
            this.outboundBuffer = null; // Disallow adding any messages and flushes to outboundBuffer.
            Executor closeExecutor = prepareToClose();
            if (closeExecutor != null) {
                closeExecutor.execute(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            // Execute the close.
                            //調用底層關閉socket
                            doClose0(promise);
                        } finally {
                            // Call invokeLater so closeAndDeregister is executed in the EventLoop again!
                            invokeLater(new Runnable() {
                                @Override
                                public void run() {
                                    if (outboundBuffer != null) {
                                        // Fail all the queued messages
                                        //刪除隊列緩存中還沒發出去的消息,釋放資源
                                        outboundBuffer.failFlushed(cause, notify);
                                        outboundBuffer.close(closeCause);
                                    }
                                    //激發fireChannelInactive事件
                                    //激發fireChannelUnregistered事件
                                    fireChannelInactiveAndDeregister(wasActive);
                                }
                            });
                        }
                    }
                });
            } else {
                try {
                    // Close the channel and fail the queued messages in all cases.
                    doClose0(promise);
                } finally {
                    if (outboundBuffer != null) {
                        // Fail all the queued messages.
                        outboundBuffer.failFlushed(cause, notify);
                        outboundBuffer.close(closeCause);
                    }
                }
                if (inFlush0) {
                    invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            fireChannelInactiveAndDeregister(wasActive);
                        }
                    });
                } else {
                    fireChannelInactiveAndDeregister(wasActive);
                }
            }
        }

 

 

技術交流QQ:212320390

關注公衆號

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