解码时序图
提供者服务和消费者服务都需要使用解码功能,下图是解码时序图:
InternalDecode
:实现了Netty
的ChannelHandler
接口,是一个解码器实现类。DubboCountCodec
:将解码结果包装到MultiMessage
。ExchangeCodec
:负责解码报文的消息头数据。DubboCodec
:根据消息类型转发给DecodeableRpcInvocation
或者DecodeableRpcResult
进行消息体数据的解码。DecodeableRpcResult
:解码响应包的消息体数据。DecodeableRpcInvocation
:解码请求包的消息体数据。
解码详细过程
下面我们来源码分析Dubbo解码过程,进入InternalDecode
的messageReceived()
方法:
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent event) throws Exception {
//......省略部分代码
NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
Object msg;
int saveReaderIndex;
try {
// decode object.
do {
//message ChannelBuffer
saveReaderIndex = message.readerIndex(); //@1
try {
msg = codec.decode(channel, message); //@2
} catch (IOException e) {
buffer = com.alibaba.dubbo.remoting.buffer.ChannelBuffers.EMPTY_BUFFER;
throw e;
}
if (msg == Codec2.DecodeResult.NEED_MORE_INPUT) {//@3
message.readerIndex(saveReaderIndex);
break;
} else {
if (saveReaderIndex == message.readerIndex()) {//@4
buffer = com.alibaba.dubbo.remoting.buffer.ChannelBuffers.EMPTY_BUFFER;
throw new IOException("Decode without read data.");
}
if (msg != null) {//@5
Channels.fireMessageReceived(ctx, msg, event.getRemoteAddress());
}
}
} while (message.readable());
} finally {
if (message.readable()) {//@6
message.discardReadBytes();
buffer = message;
} else {
buffer = com.alibaba.dubbo.remoting.buffer.ChannelBuffers.EMPTY_BUFFER;
}
NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
}
}
代码@1:message是一个ChannelBuffer,获取它的读指针下标赋值给saveReaderIndex。
代码@2:codec实例类型是DubboCountCodec,解码读取到的message内容。
代码@3:如果步骤@2的解码结果返回NEED_MORE_INPUT
,表示message内容并不是一个完整的数据包(出现粘包拆包等问题),从新将readerIndex
归位到saveReaderIndex
。
代码@4:如果两者相等,说明没有读取到数据,向外抛出异常信息。
代码@5:如果msg不为空,说明数据包已经解码成功,交给后面的Handler处理。
代码@6:message是用来存储读取到的数据的,当message不是完整数据包时如上面的步骤@3,这时候执行message.discardReadBytes()
释放读坐标和写坐标都归0,同时赋值给缓冲区buffer,下次读取到数据时再赋值给message。
真正开始解码的过程在ExchangeCodec,我们进入它的decode()
方法:
@Override
protected Object decode(Channel channel, ChannelBuffer buffer, int readable, byte[] header) throws IOException {
// check magic number.
if (readable > 0 && header[0] != MAGIC_HIGH
|| readable > 1 && header[1] != MAGIC_LOW) {//@1
int length = header.length;
if (header.length < readable) {//@2
header = Bytes.copyOf(header, readable);//@3
buffer.readBytes(header, length, readable - length);//@4
}
for (int i = 1; i < header.length - 1; i++) {
if (header[i] == MAGIC_HIGH && header[i + 1] == MAGIC_LOW) {
buffer.readerIndex(buffer.readerIndex() - header.length + i);//@5
header = Bytes.copyOf(header, i);//@6
break;
}
}
return super.decode(channel, buffer, readable, header);
}
// check length.
if (readable < HEADER_LENGTH) {//@7
return DecodeResult.NEED_MORE_INPUT;
}
// get data length.
int len = Bytes.bytes2int(header, 12);//@8
checkPayload(channel, len);
int tt = len + HEADER_LENGTH;
if (readable < tt) {//@9
return DecodeResult.NEED_MORE_INPUT;
}
// limit input stream.
ChannelBufferInputStream is = new ChannelBufferInputStream(buffer, len);
try {
return decodeBody(channel, is, header); //@10
} finally {
if (is.available() > 0) {
try {
if (logger.isWarnEnabled()) {
logger.warn("Skip input stream " + is.available());
}
StreamUtils.skipUnusedStream(is);
} catch (IOException e) {
logger.warn(e.getMessage(), e);
}
}
}
}
代码@1:处理报文数据不是以魔法数0xdabb
开头的情况。
代码@2:表示buffer
中还有数据可以读取。
代码@3:重新分配给header
数组长度延长到readable
。
代码@4:缓冲区buffer
前面16个字节数据已经赋值给了header
数组,所以这里将buffer剩余的部分也读取出来附加到header
数组,这样缓冲区的内容就全部赋值给了header
数组。
代码@5:循环header
数组,找到相邻两个字节为魔法值的位置,将buffer
的读下标指向魔法值的地方,即报文的开始位置。
代码@6:将header
数组的开始位置到到魔法值之间的这段数据,又赋值给header
数组。
代码@7:说明读取到的数据还不够,需要更多的数据。
代码@8:读取消息头的最后四个字节赋值给len
,len
表示消息体的字节长度,接着执行checkPayload()
检测消息体长度。
代码@9:tt
代表了这个数据包的总长度,如果readable
小于tt
,说明读取到的数据包还不够。
代码@10:执行DubboCodec
的decodeBody()
方法,解码消息体。
进入DubboCodec
的decodeBody()
方法:
@Override
protected Object decodeBody(Channel channel, InputStream is, byte[] header) throws IOException {
byte flag = header[2], proto = (byte) (flag & SERIALIZATION_MASK);
Serialization s = CodecSupport.getSerialization(channel.getUrl(), proto);
// get request id.
long id = Bytes.bytes2long(header, 4);
if ((flag & FLAG_REQUEST) == 0) {//消费者服务执行代码
// decode response.
Response res = new Response(id); //@1
if ((flag & FLAG_EVENT) != 0) {
res.setEvent(Response.HEARTBEAT_EVENT);
}
// get status.
byte status = header[3]; //@2
res.setStatus(status);
if (status == Response.OK) {
try {
Object data;
if (res.isHeartbeat()) {
data = decodeHeartbeatData(channel, deserialize(s, channel.getUrl(), is));
} else if (res.isEvent()) {
data = decodeEventData(channel, deserialize(s, channel.getUrl(), is));
} else {
DecodeableRpcResult result;
if (channel.getUrl().getParameter(
Constants.DECODE_IN_IO_THREAD_KEY,
Constants.DEFAULT_DECODE_IN_IO_THREAD)) {
result = new DecodeableRpcResult(channel, res, is,
(Invocation) getRequestData(id), proto);//@3
result.decode();
} else {
result = new DecodeableRpcResult(channel, res,
new UnsafeByteArrayInputStream(readMessageData(is)),
(Invocation) getRequestData(id), proto);
}
data = result;
}
res.setResult(data);
} catch (Throwable t) {
if (log.isWarnEnabled()) {
log.warn("Decode response failed: " + t.getMessage(), t);
}
res.setStatus(Response.CLIENT_ERROR);
res.setErrorMessage(StringUtils.toString(t));
}
} else {
res.setErrorMessage(deserialize(s, channel.getUrl(), is).readUTF());
}
return res;
} else {//提供者服务执行代码
// decode request.
Request req = new Request(id); //@4
req.setVersion("2.0.0");
req.setTwoWay((flag & FLAG_TWOWAY) != 0);
if ((flag & FLAG_EVENT) != 0) {
req.setEvent(Request.HEARTBEAT_EVENT);
}
try {
Object data;
if (req.isHeartbeat()) {
data = decodeHeartbeatData(channel, deserialize(s, channel.getUrl(), is));
} else if (req.isEvent()) {
data = decodeEventData(channel, deserialize(s, channel.getUrl(), is));
} else {
DecodeableRpcInvocation inv;
if (channel.getUrl().getParameter(
Constants.DECODE_IN_IO_THREAD_KEY,
Constants.DEFAULT_DECODE_IN_IO_THREAD)) {
inv = new DecodeableRpcInvocation(channel, req, is, proto);//@5
inv.decode();
} else {
inv = new DecodeableRpcInvocation(channel, req,
new UnsafeByteArrayInputStream(readMessageData(is)), proto);
}
data = inv;
}
req.setData(data);
} catch (Throwable t) {
if (log.isWarnEnabled()) {
log.warn("Decode request failed: " + t.getMessage(), t);
}
// bad request
req.setBroken(true);
req.setData(t);
}
return req;
}
}
代码@1:这里是消费者服务执行的代码,利用回传的请求id
构造Response
实例。
代码@2:获取响应报文的的状态值。
代码@3:表示直接在IO
线程执行解码操作,响应报文交给DecodeableRpcResult
处理。
代码@4:这里是提供者服务执行的代码,根据id
构建Request
实例。
代码@5:表示直接在IO
线程执行解码操作,请求报文交给DecodeableRpcInvocation
处理。
进入DecodeableRpcInvocation
的decode()
方法:
@Override
public Object decode(Channel channel, InputStream input) throws IOException {
ObjectInput in = CodecSupport.getSerialization(channel.getUrl(), serializationType)
.deserialize(channel.getUrl(), input);
//@1
setAttachment(Constants.DUBBO_VERSION_KEY, in.readUTF());
setAttachment(Constants.PATH_KEY, in.readUTF());
setAttachment(Constants.VERSION_KEY, in.readUTF());
setMethodName(in.readUTF());//@2
try {
Object[] args;
Class<?>[] pts;
String desc = in.readUTF();//@3
if (desc.length() == 0) {
pts = DubboCodec.EMPTY_CLASS_ARRAY;
args = DubboCodec.EMPTY_OBJECT_ARRAY;
} else {
pts = ReflectUtils.desc2classArray(desc);
args = new Object[pts.length];
for (int i = 0; i < args.length; i++) {
try {
args[i] = in.readObject(pts[i]);//@4
} catch (Exception e) {
if (log.isWarnEnabled()) {
log.warn("Decode argument failed: " + e.getMessage(), e);
}
}
}
}
setParameterTypes(pts);
Map<String, String> map = (Map<String, String>) in.readObject(Map.class);
if (map != null && map.size() > 0) {//@5
Map<String, String> attachment = getAttachments();
if (attachment == null) {
attachment = new HashMap<String, String>();
}
attachment.putAll(map);
setAttachments(attachment);
}
//decode argument ,may be callback
for (int i = 0; i < args.length; i++) {
args[i] = decodeInvocationArgument(channel, this, pts, i, args[i]);
}
setArguments(args);
} catch (ClassNotFoundException e) {
throw new IOException(StringUtils.toString("Read invocation data failed.", e));
} finally {
if (in instanceof Cleanable) {
((Cleanable) in).cleanup();
}
}
return this;
}
代码@1:按照Dubbo协议规定的消息体数据的顺序,依次读取Dubbo版本号、接口名称、接口版本。
代码@2:读取方法名称。
代码@3:读取方法参数类型。
代码@4:根据参数类型,依次读取方法参数值。
代码@5:读取额外的参数attachments,又被称为隐式参数。
进入DecodeableRpcResult
的decode()
方法:
@Override
public Object decode(Channel channel, InputStream input) throws IOException {
ObjectInput in = CodecSupport.getSerialization(channel.getUrl(), serializationType)
.deserialize(channel.getUrl(), input);
byte flag = in.readByte(); //@1
switch (flag) {
case DubboCodec.RESPONSE_NULL_VALUE: //@2
break;
case DubboCodec.RESPONSE_VALUE:
try {
Type[] returnType = RpcUtils.getReturnTypes(invocation);//@3
setValue(returnType == null || returnType.length == 0 ? in.readObject() :
(returnType.length == 1 ? in.readObject((Class<?>) returnType[0])
: in.readObject((Class<?>) returnType[0], returnType[1])));//@4
} catch (ClassNotFoundException e) {
throw new IOException(StringUtils.toString("Read response data failed.", e));
}
break;
case DubboCodec.RESPONSE_WITH_EXCEPTION:
try {
Object obj = in.readObject();
if (obj instanceof Throwable == false)
throw new IOException("Response data error, expect Throwable, but get " + obj);
setException((Throwable) obj);//@5
} catch (ClassNotFoundException e) {
throw new IOException(StringUtils.toString("Read response data failed.", e));
}
break;
default://@6
throw new IOException("Unknown result flag, expect '0' '1' '2', get " + flag);
}
if (in instanceof Cleanable) {
((Cleanable) in).cleanup();
}
return this;
}
代码@1:先读取一个字节赋值给flag
,上一篇文章我们介绍过,提供者服务在将结果返回时,会先往消息体写一个字节代表消息响应类型。
代码@2:如果flag
值为DubboCodec.RESPONSE_NULL_VALUE
,表示响应报文没有返回值。
代码@3:如果flag
值为DubboCodec.RESPONSE_VALUE
,表示响应报文包含返回值,获取返回值类型。
代码@4:根据返回值类型,读取返回值。
代码@5:如果flag
值为DubboCodec.RESPONSE_WITH_EXCEPTION
,表示响应报文返回值是异常信息。
代码@6:不识别的消息响应类型,向外抛出IOException
异常信息。