在幾個月前改造dubbo時,netty4已經穩定很久了,一時手癢,按照netty3-rpc的源碼克隆了一套netty4,在修正了大量的包、類型不同之后,基本保持了netty3的風格,并發量小或者數據包很小時,一切都很ok, 在進行大并發測試時,結果和netty3完全不同,基本用慘不忍睹來形容。由于當時急于開發php客戶端,就把netty4-rpc當做一個失敗的組件存檔起來, 前幾天php-dubbo開發基本完成之后,返回過來思考netty4-rpc的問題,經過仔細分析數據包的解析過程,單步跟蹤源碼

NettyCodecAdapter, TelnetCodec, ExchangeCoedec,發現ByteBuf的緩沖區為1024,當數據超過1024時,會調用多次Decoder.messageReceived函數,第一次分析dubbo的協議頭時,是正確的,第二次之后數據就錯誤了,然后dubbo內部緩沖區的數據越來越長,但是仍然分析不到一個完整的dubbo數據包

因此去看netty4的源碼,發現AbstractNioByteChannel中有網絡數據接收的代碼時這么處理ByteBuf的

  ByteBuf byteBuf = null;
            int messages = 0;
            boolean close = false;
            try {
                int totalReadAmount = 0;
                boolean readPendingReset = false;
                do {
                    byteBuf = allocHandle.allocate(allocator);
                    int writable = byteBuf.writableBytes();
                    int localReadAmount = doReadBytes(byteBuf);
                    if (localReadAmount <= 0) {
                        // not was read release the buffer
                        byteBuf.release();
                        close = localReadAmount < 0;
                        break;
                    }
                    if (!readPendingReset) {
                        readPendingReset = true;
                        setReadPending(false);
                    }
                    pipeline.fireChannelRead(byteBuf);
                    byteBuf = null;

看見沒,內核是需要ByteBuf.release的,繼續通過byteBuf的一個實現PooledByteBuf分析源碼,原來是實現了一個基于簡單計數應用計數的循環使用的緩沖區,一旦計數變為1,該緩沖區被歸還到netty4內核,被后面的數據讀取線程重新使用

而我們InternalDecoder的代碼為

      message = com.alibaba.dubbo.remoting.buffer.ChannelBuffers.wrappedBuffer(
                    input.toByteBuffer());

直接引用了ByteBuf.toByteBuffer,繼續查看源碼UnpooledHeapByteBuf, 其toByteBuffer實際是對內部數據的

一個nio封裝而已,因此,使用上述函數時,導致dubbo的decode保存了一個某一個ByteBuffer的內部數據,但是雖有該

buffer被歸還到netty4緩沖區中被循環引用,下一次可能被其他讀寫線程重新改寫數據,因此,高并發下當緩沖區被重復使用時,bytebuf將由于計數問題不斷被使用,而解碼器中缺傻傻等待。

解決方案

1.通過byteBuf的retain和release函數保證計數的有效性,通過程序例外或者緩沖區被使用完成時候歸還ByteBuf到netty4內核

2.拷貝數據到dubbo的緩沖區中

思考:

netty3 是否也有該問題呢???