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

NettyCodecAdapter, TelnetCodec, ExchangeCoedec,發(fā)現(xiàn)ByteBuf的緩沖區(qū)為1024,當(dāng)數(shù)據(jù)超過1024時,會調(diào)用多次Decoder.messageReceived函數(shù),第一次分析dubbo的協(xié)議頭時,是正確的,第二次之后數(shù)據(jù)就錯誤了,然后dubbo內(nèi)部緩沖區(qū)的數(shù)據(jù)越來越長,但是仍然分析不到一個完整的dubbo數(shù)據(jù)包

因此去看netty4的源碼,發(fā)現(xiàn)AbstractNioByteChannel中有網(wǎng)絡(luò)數(shù)據(jù)接收的代碼時這么處理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;

看見沒,內(nèi)核是需要ByteBuf.release的,繼續(xù)通過byteBuf的一個實(shí)現(xiàn)PooledByteBuf分析源碼,原來是實(shí)現(xiàn)了一個基于簡單計(jì)數(shù)應(yīng)用計(jì)數(shù)的循環(huán)使用的緩沖區(qū),一旦計(jì)數(shù)變?yōu)?,該緩沖區(qū)被歸還到netty4內(nèi)核,被后面的數(shù)據(jù)讀取線程重新使用

而我們InternalDecoder的代碼為

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

直接引用了ByteBuf.toByteBuffer,繼續(xù)查看源碼UnpooledHeapByteBuf, 其toByteBuffer實(shí)際是對內(nèi)部數(shù)據(jù)的

一個nio封裝而已,因此,使用上述函數(shù)時,導(dǎo)致dubbo的decode保存了一個某一個ByteBuffer的內(nèi)部數(shù)據(jù),但是雖有該

buffer被歸還到netty4緩沖區(qū)中被循環(huán)引用,下一次可能被其他讀寫線程重新改寫數(shù)據(jù),因此,高并發(fā)下當(dāng)緩沖區(qū)被重復(fù)使用時,bytebuf將由于計(jì)數(shù)問題不斷被使用,而解碼器中缺傻傻等待。

解決方案

1.通過byteBuf的retain和release函數(shù)保證計(jì)數(shù)的有效性,通過程序例外或者緩沖區(qū)被使用完成時候歸還ByteBuf到netty4內(nèi)核

2.拷貝數(shù)據(jù)到dubbo的緩沖區(qū)中

思考:

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