dubbo在通信上也支持非常多的网络协议,而dubbo协议属于dubbo框架自研,整体协议也比较有代表性,采用定长协议头+变长协议休的形式;
1. dubbo协议格式
Magic - Magic High & Magic Low (16 bits)
标识协议版本号,Dubbo 协议:0xdabb
Req/Res (1 bit)
标识是请求或响应。请求: 1; 响应: 0。
2 Way (1 bit)
仅在 Req/Res 为1(请求)时才有用,标记是否期望从服务器返回值。如果需要来自服务器的返回值,则设置为1。
Event (1 bit)
标识是否是事件消息,例如,心跳事件。如果这是一个事件,则设置为1。
Serialization ID (5 bit)
标识序列化类型:比如 fastjson 的值为6。
Status (8 bits)
仅在 Req/Res 为0(响应)时有用,用于标识响应的状态。
- 20 - OK
- 30 - CLIENT_TIMEOUT
- 31 - SERVER_TIMEOUT
- 40 - BAD_REQUEST
- 50 - BAD_RESPONSE
- 60 - SERVICE_NOT_FOUND
- 70 - SERVICE_ERROR
- 80 - SERVER_ERROR
- 90 - CLIENT_ERROR
- 100 - SERVER_THREADPOOL_EXHAUSTED_ERROR
Request ID (64 bits)
标识唯一请求。类型为long。
Data Length (32 bits)
序列化后的内容长度(可变部分),按字节计数。int类型。
Variable Part
被特定的序列化类型(由序列化 ID 标识)序列化后,每个部分都是一个 byte [] 或者 byte
- 如果是请求包 ( Req/Res = 1),则每个部分依次为:
- Dubbo version
- Service name
- Service version
- Method name
- Method parameter types
- Method arguments
- Attachments
- 如果是响应包(Req/Res = 0),则每个部分依次为:
- 返回值类型(byte),标识从服务器端返回的值类型:
- 返回空值:RESPONSE_NULL_VALUE 2
- 正常响应值: RESPONSE_VALUE 1
- 异常:RESPONSE_WITH_EXCEPTION 0
- 返回值:从服务端返回的响应bytes
- 返回值类型(byte),标识从服务器端返回的值类型:
- 如果是请求包 ( Req/Res = 1),则每个部分依次为:
2. 消费端网络请求源码分析
org.apache.dubbo.remoting.Codec2定义为I/O的 Codec 过程,因此主要的方法就是encode和decode;
网络发送的源码入口在:com.alibaba.dubbo.remoting.transport.netty4.NettyChannel#send
这是一个调用链路,从inovker到网络请求的具体流程;
final NettyClientHandler nettyClientHandler = new NettyClientHandler(getUrl(), this);
bootstrap.handler(new ChannelInitializer() {
@Override
protected void initChannel(Channel ch) throws Exception {
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this);
ch.pipeline()//.addLast("logging",new LoggingHandler(LogLevel.INFO))//for debug
.addLast("decoder", adapter.getDecoder())
.addLast("encoder", adapter.getEncoder())
.addLast("handler", nettyClientHandler);
String socksProxyHost = ConfigUtils.getProperty(SOCKS_PROXY_HOST);
if(socksProxyHost != null) {
int socksProxyPort = Integer.parseInt(ConfigUtils.getProperty(SOCKS_PROXY_PORT, DEFAULT_SOCKS_PROXY_PORT));
Socks5ProxyHandler socks5ProxyHandler = new Socks5ProxyHandler(new InetSocketAddress(socksProxyHost, socksProxyPort));
ch.pipeline().addFirst(socks5ProxyHandler);
}
}
});
对于消费端,也就是网络out的流程:nettyClientHandler->encoder;调用nettyClientHandler处理业务逻辑,然后调用encoder进行编码,将编码的结果传输到服务提供者中;
adapter.getEncoder()
会调用到:
// codec2的获取逻辑,默认获取telnet,而Codec2有dubbo协议的实现
protected static Codec2 getChannelCodec(URL url) {
String codecName = url.getParameter(Constants.CODEC_KEY, "telnet");
if (ExtensionLoader.getExtensionLoader(Codec2.class).hasExtension(codecName)) {
return ExtensionLoader.getExtensionLoader(Codec2.class).getExtension(codecName);
} else {
return new CodecAdapter(ExtensionLoader.getExtensionLoader(Codec.class)
.getExtension(codecName));
}
}
private class InternalEncoder extends MessageToByteEncoder {
@Override
protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
com.alibaba.dubbo.remoting.buffer.ChannelBuffer buffer = new NettyBackedChannelBuffer(out);
Channel ch = ctx.channel();
NettyChannel channel = NettyChannel.getOrAddChannel(ch, url, handler);
try {
// 所以这里的codec获取就是dubboCodec
codec.encode(channel, buffer, msg);
} finally {
NettyChannel.removeChannelIfDisconnected(ch);
}
}
}
dubboCodec.encode
dubboCodec的encode由父类ExchangeCodec
实现:
@Override
public void encode(Channel channel, ChannelBuffer buffer, Object msg) throws IOException {
// 它会根据msg类型判断是对请求还是响应编码;
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);
}
}
encodeRequest
protected void encodeRequest(Channel channel, ChannelBuffer buffer, Request req) throws IOException {
// 获取序列化框架,默认为Hessian
Serialization serialization = getSerialization(channel);
// header.
// HEADER_LENGTH=16字节
byte[] header = new byte[HEADER_LENGTH];
// set magic number.
// 写入固定的魔数;
Bytes.short2bytes(MAGIC, header);
// set request and serialization flag.
// 设置序列化id号
header[2] = (byte) (FLAG_REQUEST | serialization.getContentTypeId());
// 设置是否期望返回标识
if (req.isTwoWay()) header[2] |= FLAG_TWOWAY;
// 设置事件类型
if (req.isEvent()) header[2] |= FLAG_EVENT;
// 设置请求id
Bytes.long2bytes(req.getId(), header, 4);
// encode request data.
// 记录buffer中设置header的位置;
int savedWriteIndex = buffer.writerIndex();
// 预留出header的长度;
buffer.writerIndex(savedWriteIndex + HEADER_LENGTH);
ChannelBufferOutputStream bos = new ChannelBufferOutputStream(buffer);
// 将bos设置到out中;
ObjectOutput out = serialization.serialize(channel.getUrl(), bos);
if (req.isEvent()) {
encodeEventData(channel, out, req.getData());
} else {
// 向output中写入body
encodeRequestData(channel, out, req.getData(), req.getVersion());
}
out.flushBuffer();
if (out instanceof Cleanable) {
((Cleanable) out).cleanup();
}
bos.flush();
bos.close();
int len = bos.writtenBytes();
checkPayload(channel, len);
// 设置数据长度
Bytes.int2bytes(len, header, 12);
// write
// 设置writerIndex索引位置,将header设置到这里;
buffer.writerIndex(savedWriteIndex);
buffer.writeBytes(header); // write header.
// 设置writerIndex的新位置;
buffer.writerIndex(savedWriteIndex + HEADER_LENGTH + len);
}
// DubboCodec
@Override
protected void encodeRequestData(Channel channel, ObjectOutput out, Object data, String version) throws IOException {
RpcInvocation inv = (RpcInvocation) data;
out.writeUTF(version);
// path
out.writeUTF(inv.getAttachment(Constants.PATH_KEY