5.dubbo源碼-編碼&解碼

Netty編碼申明

由于dubbo底層使用Netty作為網絡傳輸框架,所以如果需要編碼的話,可以通過繼承netty的org.jboss.netty.handler.codec.oneone.OneToOneEncoder實現。dubbo編碼實現在NettyCodecAdapter中(通過發布服務分析可知),自定義編碼實現部分源碼如下:

@Sharable
private class InternalEncoder extends OneToOneEncoder {
    @Override
    protected Object encode(ChannelHandlerContext ctx, Channel ch, Object msg) throws Exception{
        com.alibaba.dubbo.remoting.buffer.ChannelBuffer buffer =
            com.alibaba.dubbo.remoting.buffer.ChannelBuffers.dynamicBuffer(1024);
        NettyChannel channel = NettyChannel.getOrAddChannel(ch, url, handler);
        try {
            codec.encode(channel, buffer, msg);
        } finally {
            NettyChannel.removeChannelIfDisconnected(ch);
        }
        return ChannelBuffers.wrappedBuffer(buffer.toByteBuffer());
    }
}

codec默認實現

dubbo-rpc-default模塊中/META-INF/dubbo/internal目錄下文件com.alibaba.dubbo.remoting.Codec2中指定:
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboCountCodec
所以codec.encode(channel, buffer, msg);
--> DubboCountCodec.encode(Channel channel, ChannelBuffer buffer, Object msg);
--> ExchangeCodec.encode(Channel channel, ChannelBuffer buffer, Object msg),
部分源碼如下:

public void encode(Channel channel, ChannelBuffer buffer, Object msg) throws IOException {
    if (msg instanceof Request) {
        encodeRequest(channel, buffer, (Request) msg);
    } else if (msg instanceof Response) {
        encodeResponse(channel, buffer, (Response) msg);
    } else {
        super.encode(channel, buffer, msg);
    }
}

以consumer調用provider為例,那么msg就是Request類型:所以接下來執行encodeRequest(channel, buffer, (Request) msg),這里執行的主要的業務邏輯:

  1. 獲取具體的序列化實現,默認為hessian2:Serialization serialization = getSerialization(channel);
  2. 組裝協議頭
  3. 組裝協議體encodeRequestData(channel, out, req.getData())并序列化寫入buffer;
  4. 檢查寫入數據是否超載,默認為8M,可以通過payload設置,checkPayload(channel, len);
  5. 將協議頭寫入buffer;
  6. 在buffer上設置寫的index;

編碼-組裝協議頭

源碼申明如下:byte[] header = new byte[HEADER_LENGTH];,所以協議頭總計有16個byte;</br>
第1步:</br>
Bytes.short2bytes(MAGIC, header);
所以協議頭的前2個字節由MAGIC指定;</br>
第2步:</br>
header[2] = (byte) (FLAG_REQUEST | serialization.getContentTypeId());
第2個字節的計算方式,</br>
第3步:</br>
Bytes.long2bytes(req.getId(), header, 4);
請求id賦值到消息頭的4~11位置,占8個字節;</br>
第4步:</br>
Bytes.int2bytes(len, header, 12);
消息體長度賦值到消息頭的12~15位置,占4個字節;</br>

編碼-組裝協議體

實現源碼在DubboCodec.encodeRequestData(Channel channel, ObjectOutput out, Object data):

@Override
protected void encodeRequestData(Channel channel, ObjectOutput out, Object data) throws IOException {
    RpcInvocation inv = (RpcInvocation) data;
    out.writeUTF(inv.getAttachment(Constants.DUBBO_VERSION_KEY, DUBBO_VERSION));
    out.writeUTF(inv.getAttachment(Constants.PATH_KEY));
    out.writeUTF(inv.getAttachment(Constants.VERSION_KEY));
    out.writeUTF(inv.getMethodName());
    out.writeUTF(ReflectUtils.getDesc(inv.getParameterTypes()));
    Object[] args = inv.getArguments();
    if (args != null)
    for (int i = 0; i < args.length; i++){
        out.writeObject(encodeInvocationArgument(channel, inv, i));
    }
    out.writeObject(inv.getAttachments());
}

由源碼可知,消息體的內容如下:</br>
1、dubbo版本號,例如:2.0.0</br>
2、invoke的路徑,例如:com.alibaba.dubbo.demo.TestService</br>
3、invoke的provider端暴露的服務的版本號, 例如:0.0.0</br>
4、調用的方法名稱,例如:getTeacher</br>
5、參數類型描述符,例如:Lcom/alibaba/dubbo/demo/bean/Student;</br>
6、遍歷請求參數值并編碼;</br>
7、dubbo請求的attachments:</br>

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容