Dubbo编解码系列文章目录
Dubbo编解码(一)-原理
Dubbo编解码(二)-Codec2和AbstractCodec
Dubbo编解码(三)-TransportCodec
Dubbo编解码(五)-Dubbo协议编码器
Dubbo编解码(六)-Dubbo协议解码器
文章目录
ExchangeCodec#encode
覆写Codec2#encode实现
@Override
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);
}
}
encodeRequest-编码 请求对象
/**
* 对 请求对象 进行编码,然后写入到buffer中
*
* @param channel 主要用于获取信息
* @param buffer 最终要写入的通道缓存
* @param req 请求对象
* @throws IOException IOException
*/
protected void encodeRequest(Channel channel, ChannelBuffer buffer, Request req) throws IOException {
// 根据通道channel获取序列化实例类
// AbstractCodec#getSerialization
Serialization serialization = getSerialization(channel, req);
// header.
// 构架header,长度16
byte[] header = new byte[HEADER_LENGTH];
// set magic number.
// 设置魔法值,占用2个字节
Bytes.short2bytes(MAGIC, header);
// set request and serialization flag.
// 在第3个字节设置 请求标识 + 序列化标识
header[2] = (byte) (FLAG_REQUEST | serialization.getContentTypeId());
// 双向传输
if (req.isTwoWay()) {
header[2] |= FLAG_TWOWAY;
}
// 心跳事件
if (req.isEvent()) {
header[2] |= FLAG_EVENT;
}
// set request id.
// 设置requestId
Bytes.long2bytes(req.getId(), header, 4);
// encode request data.
// writerIndex-写缓冲区首地址
int savedWriteIndex = buffer.writerIndex();
// 移动writeIndex,跳过buffer头部的16个字节,为的是下面往buffer写入请求体
buffer.writerIndex(savedWriteIndex + HEADER_LENGTH);
// 通道缓存输出流,在 ChannelBufferOutputStream类型对象 里面对buffer进行写入
ChannelBufferOutputStream bos = new ChannelBufferOutputStream(buffer);
// 心跳请求,写入一个空字节数组
if (req.isHeartbeat()) {
// heartbeat request data is always null
// 通过getNullBytesOf获取一个空数组,再通过write方法写入到buffer中
// ChannelBufferOutputStream#write 实际就是buffer#writeBytes
bos.write(CodecSupport.getNullBytesOf(serialization));
} else {
// 对bos序列化,主要就是 把 ChannelBufferOutputStream类型对象 放到 ObjectOutput类型对象 中
// 本质上就是把 OutputStream类型对象 放到 ObjectOutput类型对象 中
ObjectOutput out = serialization.serialize(channel.getUrl(), bos);
// 心跳事件
if (req.isEvent()) {
// 心跳事件请求体编码,向out中写入req.getData()
// TODO 心跳事件请求体是什么?
encodeEventData(channel, out, req.getData());
} else {
// 编码请求体,并写入out
encodeRequestData(channel, out, req.getData(), req.getVersion());
}
// OutputStream#flush()
out.flushBuffer();
if (out instanceof Cleanable) {
((Cleanable) out).cleanup();
}
}
bos.flush();
bos.close();
// bos长度
int len = bos.writtenBytes();
// 校验长度是否符合协议规范
checkPayload(channel, len);
// 把body的长度(len)写入header的第12个字节
Bytes.int2bytes(len, header, 12);
// write
// 定位指针到buffer开始的地方
buffer.writerIndex(savedWriteIndex);
// 写入header
buffer.writeBytes(header); // write header.
// 设置writerIndex到消息体结束的位置
buffer.writerIndex(savedWriteIndex + HEADER_LENGTH + len);
}
CodecSupport#getNullBytesOf
/**
* Get the null object serialize result byte[] of Serialization from the cache,
* if not, generate it first.
* 从缓存中获取 空对象序列化结果-字节数组,如果没有就生成一个
*
* @param s Serialization Instances
* @return serialize result of null object
*/
public static byte[] getNullBytesOf(Serialization s) {
return ID_NULLBYTES_MAP.computeIfAbsent(s.getContentTypeId(), k -> {
//Pre-generated Null object bytes
// 初始化一个容量为32的字节数组
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] nullBytes = new byte[0];
try {
ObjectOutput out = s.serialize(null, baos);
// 就是写一个null到baos中
out.writeObject(null);
// OutputStream#flush()
out.flushBuffer();
nullBytes = baos.toByteArray();
baos.close();
} catch (Exception e) {
logger.warn("Serialization extension " + s.getClass().getName() + " not support serializing null object, return an empty bytes instead.");
}
return nullBytes;
});
}
DubboCodec#encodeRequestData-编码 请求对象体
/**
* 向out中写入 请求体信息
*
* @param channel 通道
* @param out 要写入的输出对象
* @param data 请求体数据
* @param version 版本号
* @throws IOException IOException
*/
@Override
protected void encodeRequestData(Channel channel, ObjectOutput out, Object data, String version) throws IOException {
RpcInvocation inv = (RpcInvocation) data;
// 写入版本号,用于支持 服务端版本隔离 和 服务端隐式参数 传递给客户端的特性
out.writeUTF(version);
// https://github.com/apache/dubbo/issues/6138
// 写入调用接口
String serviceName = inv.getAttachment(INTERFACE_KEY);
if (serviceName == null) {
serviceName = inv.getAttachment(PATH_KEY);
}
out.writeUTF(serviceName);
// 写入接口指定的版本号
out.writeUTF(inv.getAttachment(VERSION_KEY));
// 写入方法名称
out.writeUTF(inv.getMethodName());
// 写入参数类型
out.writeUTF(inv.getParameterTypesDesc());
// 循环写入参数值
Object[] args = inv.getArguments();
if (args != null) {
for (int i = 0; i < args.length; i++) {
// encodeInvocationArgument TODO
out.writeObject(encodeInvocationArgument(channel, inv, i));
}
}
// 写入隐式参数
out.writeAttachments(inv.getObjectAttachments());
}
encodeResponse-编码 响应对象
/**
* 对 响应对象 进行编码,然后写入到buffer中
*
* @param channel 主要用于获取信息
* @param buffer 最终要写入的通道缓存
* @param res 响应对象
* @throws IOException IOException
*/
protected void encodeResponse(Channel channel, ChannelBuffer buffer, Response res) throws IOException {
int savedWriteIndex = buffer.writerIndex();
try {
Serialization serialization = getSerialization(channel, res);
// header.
byte[] header = new byte[HEADER_LENGTH];
// set magic number.
Bytes.short2bytes(MAGIC, header);
// set request and serialization flag.
header[2] = serialization.getContentTypeId();
if (res.isHeartbeat()) {
header[2] |= FLAG_EVENT;
}
// set response status.
// 在第4个字节存储响应状态
byte status = res.getStatus();
header[3] = status;
// set request id.
Bytes.long2bytes(res.getId(), header, 4);
buffer.writerIndex(savedWriteIndex + HEADER_LENGTH);
ChannelBufferOutputStream bos = new ChannelBufferOutputStream(buffer);
// encode response data or error message.
if (status == Response.OK) {
if(res.isHeartbeat()){
// heartbeat response data is always null
bos.write(CodecSupport.getNullBytesOf(serialization));
}else {
ObjectOutput out = serialization.serialize(channel.getUrl(), bos);
if (res.isEvent()) {
encodeEventData(channel, out, res.getResult());
} else {
// 编码响应体
encodeResponseData(channel, out, res.getResult(), res.getVersion());
}
out.flushBuffer();
if (out instanceof Cleanable) {
((Cleanable) out).cleanup();
}
}
} else {
// 写入失败信息
ObjectOutput out = serialization.serialize(channel.getUrl(), bos);
out.writeUTF(res.getErrorMessage());
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
buffer.writerIndex(savedWriteIndex);
buffer.writeBytes(header); // write header.
buffer.writerIndex(savedWriteIndex + HEADER_LENGTH + len);
} catch (Throwable t) {
// clear buffer
// 如果编码失败,则复位buffer
buffer.writerIndex(savedWriteIndex);
// send error message to Consumer, otherwise, Consumer will wait till timeout.
// 将编码响应异常 发送给consumer,否则只能等到超时
if (!res.isEvent() && res.getStatus() != Response.BAD_RESPONSE) {
Response r = new Response(res.getId(), res.getVersion());
r.setStatus(Response.BAD_RESPONSE);
// 超过payload限制
if (t instanceof ExceedPayloadLimitException) {
logger.warn(t.getMessage(), t);
try {
r.setErrorMessage(t.getMessage());
// 告知 客户端 数据包长度 超过限制
channel.send(r);
return;
} catch (RemotingException e) {
logger.warn("Failed to send bad_response info back: " + t.getMessage() + ", cause: " + e.getMessage(), e);
}
} else {
// FIXME log error message in Codec and handle in caught() of IoHanndler?
logger.warn("Fail to encode response: " + res + ", send bad_response info instead, cause: " + t.getMessage(), t);
try {
// 告知 客户端 编码失败的具体原因
r.setErrorMessage("Failed to send response: " + res + ", cause: " + StringUtils.toString(t));
channel.send(r);
return;
} catch (RemotingException e) {
logger.warn("Failed to send bad_response info back: " + res + ", cause: " + e.getMessage(), e);
}
}
}
// Rethrow exception
if (t instanceof IOException) {
throw (IOException) t;
} else if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else if (t instanceof Error) {
throw (Error) t;
} else {
throw new RuntimeException(t.getMessage(), t);
}
}
}
DubboCodec#encodeResponseData-编码 响应对象体
/**
* 向out中写入 响应体信息
*
* @param channel 通道
* @param out 要写入的输出对象
* @param data 响应体数据
* @param version 版本号
* @throws IOException IOException
*/
@Override
protected void encodeResponseData(Channel channel, ObjectOutput out, Object data, String version) throws IOException {
Result result = (Result) data;
// currently, the version value in Response records the version of Request
// 判断 客户端请求的版本 是否支持 服务端 参数返回
boolean attach = Version.isSupportResponseAttachment(version);
// 获取 结果中的异常信息
Throwable th = result.getException();
if (th == null) {
// 提取正常返回结果
Object ret = result.getValue();
if (ret == null) {
out.writeByte(attach ? RESPONSE_NULL_VALUE_WITH_ATTACHMENTS : RESPONSE_NULL_VALUE);
} else {
// 在 编码结果 前,先写一个 字节标志
out.writeByte(attach ? RESPONSE_VALUE_WITH_ATTACHMENTS : RESPONSE_VALUE);
// 写调用结果
out.writeObject(ret);
}
} else {
// 标记调用异常,序列化异常
out.writeByte(attach ? RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS : RESPONSE_WITH_EXCEPTION);
out.writeThrowable(th);
}
if (attach) {
// returns current version of Response to consumer side.
// 返回dubbo的版本给消费端
result.getObjectAttachments().put(DUBBO_VERSION_KEY, Version.getProtocolVersion());
// 返回服务隐式参数
out.writeAttachments(result.getObjectAttachments());
}
}