Dubbo编解码(五)-Dubbo协议编码器

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());
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值