5.dubbo源码-编码&解码

2017-11-06  本文已影响262人  阿飞的博客

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>

上一篇下一篇

猜你喜欢

热点阅读