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),这里执行的主要的业务逻辑:
- 获取具体的序列化实现,默认为hessian2:Serialization serialization = getSerialization(channel);
- 组装协议头
- 组装协议体encodeRequestData(channel, out, req.getData())并序列化写入buffer;
- 检查写入数据是否超载,默认为8M,可以通过payload设置,checkPayload(channel, len);
- 将协议头写入buffer;
- 在buffer上设置写的index;
编码-组装协议头
源码申明如下:byte[] header = new byte[HEADER_LENGTH];
,所以协议头总计有16个byte;
第1步:
Bytes.short2bytes(MAGIC, header);
所以协议头的前2个字节由MAGIC指定;
第2步:
header[2] = (byte) (FLAG_REQUEST | serialization.getContentTypeId());
第2个字节的计算方式,
第3步:
Bytes.long2bytes(req.getId(), header, 4);
请求id赋值到消息头的4~11位置,占8个字节;
第4步:
Bytes.int2bytes(len, header, 12);
消息体长度赋值到消息头的12~15位置,占4个字节;
编码-组装协议体
实现源码在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());
}
由源码可知,消息体的内容如下:
1、dubbo版本号,例如:2.0.0
2、invoke的路径,例如:com.alibaba.dubbo.demo.TestService
3、invoke的provider端暴露的服务的版本号, 例如:0.0.0
4、调用的方法名称,例如:getTeacher
5、参数类型描述符,例如:Lcom/alibaba/dubbo/demo/bean/Student;
6、遍历请求参数值并编码;
7、dubbo请求的attachments: