Netty&RocketMQ&Dubboの粘着バッグと取り外し


1.接着と取り外しの原因
TCPはストリームプロトコルであり,限界のない一連のデータである.TCP下位層は上位層のトラフィックデータの具体的な意味を理解しておらず、TCPバッファの実際の状況に応じてパケット分割されるため、トラフィック上は1つの完全なパケットがTCPによって複数のパケットに分割されて送信される可能性があり、複数の小さなパケットを1つの大きなデータパケットにカプセル化する可能性があると考えられている.これがTCPのボンドとアンラップの問題です.(HTTPはなぜないのか)
HTTPもTCP伝送メカニズムを用いているが,tcpを利用してデータを送信するたびに相手と接続を確立し,双方がデータを送信して手を振って切断するとHTTPのようなことは起こらない.データ送信&受信が完了してアクティブに接続を閉じるか、ファイル格納が完了するため、送信者はFTPのように送信するだけでよい.
理由をまとめると、1.アプリケーションwriteが書き込むバイトサイズは、ソケット送信キャッシュ領域サイズより大きい.
2.MSSサイズのTCPセグメント??を行う
3.イーサネットのpayloadはMTUより大きくスライス??
Netty & RocketMQ & Dubbo的粘包和拆包_第1张图片
 
解決策
1.メッセージ固定
2.包尾にリターン改行を追加してFTPなどの分割を行う
3.メッセージをメッセージヘッダとメッセージボディに分け、メッセージヘッダはメッセージの全長を含む
 
 Netty解決の道
LineBaseFrameDecoder() + StringDecoder()
.childHandler(new ChildChannelHandler(){
     protected void initChannel(Socket args){
        args.pipline().addLast(new LineBaseFrameDecoder(1024));
        args.pipline().addLast(new StringDecoder(1024));
     }
})

LineBaseFrameDecoderの動作原理は、ByteBufの読み取り可能なバイトを1回巡って改行文字「」または「r」があるかどうかを判断し、その位置を終了位置とすることです.StringDecoder()機能は、受信したオブジェクトを文字列に変換することです.改行されていない場合は、複数のNettyを考慮したTCPボンド/パケット解除デコーダ.セパレータデコーダ
 
RocketMQ解決の道
RocketMQは主にLengFieldBasedFrameDecoderデコーダを使用している.そのメッセージの構造は以下の通りです.
エンコーディング
@Override
    public void encode(ChannelHandlerContext ctx, RemotingCommand remotingCommand, ByteBuf out)
        throws Exception {
        try {
            ByteBuffer header = remotingCommand.encodeHeader();
            out.writeBytes(header);
            byte[] body = remotingCommand.getBody();
            if (body != null) {
                out.writeBytes(body);
            }
        } catch (Exception e) {
            
        }
    }

public ByteBuffer encodeHeader(final int bodyLength) {
        // 1> header length size
        int length = 4;
        // 2> header data length
        byte[] headerData;
        headerData = this.headerEncode();
        length += headerData.length;
        // 3> body data length
        length += bodyLength;
        ByteBuffer result = ByteBuffer.allocate(4 + length - bodyLength);
        // length  Netty              =4+h.length+b.length
        result.putInt(length);
        // header length
        result.put(markProtocolType(headerData.length, serializeTypeCurrentRPC));
        // header data
        result.put(headerData);
        result.flip();
        return result;
    }

public static byte[] markProtocolType(int source, SerializeType type) {
    // 4B
        byte[] result = new byte[4];

    //   
        result[0] = type.getCode();  //         
        result[1] = (byte) ((source >> 16) & 0xFF); //        
        result[2] = (byte) ((source >> 8) & 0xFF);
        result[3] = (byte) (source & 0xFF);
        return result;
    }

デコード
public static RemotingCommand decode(final ByteBuffer byteBuffer) {
        int length = byteBuffer.limit();
    //         
        int oriHeaderLen = byteBuffer.getInt();
    //      & 0xFFFFFF    24 
        int headerLength = getHeaderLength(oriHeaderLen);
    // get()   
        byte[] headerData = new byte[headerLength];
        byteBuffer.get(headerData);
        RemotingCommand cmd = headerDecode(headerData, getProtocolType(oriHeaderLen));

    // get Body  
        int bodyLength = length - 4 - headerLength;
        byte[] bodyData = null;
        if (bodyLength > 0) {
            bodyData = new byte[bodyLength];
            byteBuffer.get(bodyData);
        }
        cmd.body = bodyData;

        return cmd;
    }

Dubbo解決の道
Dubboは自分でコーデックプロトコルを制御ことによって粘着パケットの分解問題を解決し、使用方式はLengFieldBasedFrameDecoderと似ている.
Dubboプロトコルを見てみましょう
Netty & RocketMQ & Dubbo的粘包和拆包_第2张图片
Dubboは、TCPが入るたびにデータを検証し、12-16バイト、すなわちメッセージの全長を読み取った後、読み取ったデータストリームと比較して大きくなると次のパケットが来るのを待って再度判断し、そうでないと、パケットを取り外して読み取りを開始して読み取りを記録する位置が発生しない場合、このときに驚いたメッセージを読み取ったときにデータが不足するとパケットが付着し、では、読み出した位置を書き、ループを飛び出して次のパケットの到来を待つ.
 
 
TCPデータ転送中にパケットが失われて再発生したらどうするのかという問題があります.