我们先回顾一下netty server端的handler链条初始化。
bootstrap.group(bossGroup, workerGroup)
.channel(NettyEventLoopFactory.serverSocketChannelClass())
//一般来说,一个端口释放后会等待两分钟之后才能再被使用,SO_REUSEADDR是让端口释放后立即就可以被再次使用,用于断开后的重连。
.option(ChannelOption.SO_REUSEADDR, Boolean.TRUE)
// 不启用开启Nagle算法,Nagle算法会收集网络小包再一次性发送,不启用则是即时发送,提高时效性
.childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
// ByteBuf的分配器(重用缓冲区),也就是使用对象池重复利用内存块
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// FIXME: should we use getTimeout()?
int idleTimeout = UrlUtils.getIdleTimeout(getUrl());
// 这里的getCodec()返回的就是DubboCodec
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
// 默认不启用SSL
if (getUrl().getParameter(SSL_ENABLED_KEY, false)) {
ch.pipeline().addLast("negotiation",
SslHandlerInitializer.sslServerHandler(getUrl(), nettyServerHandler));
}
ch.pipeline()
// 解码器
.addLast("decoder", adapter.getDecoder())
// 编码器
.addLast("encoder", adapter.getEncoder())
// 心跳检查handler
.addLast("server-idle-handler", new IdleStateHandler(0, 0, idleTimeout, MILLISECONDS))
// 业务处理handler
.addLast("handler", nettyServerHandler);
}
});
我们再跟进NettyCodecAdapter的处理:
final public class NettyCodecAdapter {
private final ChannelHandler encoder = new InternalEncoder();
private final ChannelHandler decoder = new InternalDecoder();
private final Codec2 codec;
private final URL url;
private final org.apache.dubbo.remoting.ChannelHandler handler;
public NettyCodecAdapter(Codec2 codec, URL url, org.apache.dubbo.remoting.ChannelHandler handler) {
this.codec = codec;
this.url = url;
this.handler = handler;
}
public ChannelHandler getEncoder() {
return encoder;
}
public ChannelHandler getDecoder() {
return decoder;
}
private class InternalEncoder extends MessageToByteEncoder {
@Override
protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
org.apache.dubbo.remoting.buffer.ChannelBuffer buffer = new NettyBackedChannelBuffer(out);
Channel ch = ctx.channel();
NettyChannel channel = NettyChannel.getOrAddChannel(ch, url, handler);
// 这里的编码就是直接调用DubboCodec的encode
codec.encode(channel, buffer, msg);
}
}
}
所以我们进入DubboCodec找一下encode()的实现,可以看到其实是在父类ExchangeCodec实现的。
public class ExchangeCodec extends TelnetCodec {
@Override
public void encode(Channel channel, ChannelBuffer buffer, Object msg) throws IOException {
// 1、对请求信息进行编码
if (msg instanceof Request) {
encodeRequest(channel, buffer, (Request) msg);
}
// 2、对响应信息进行编码
else if (msg instanceof Response) {
encodeResponse(channel, buffer, (Response) msg);
}
// 3、其他信息进行编码
else {
super.encode(channel, buffer, msg);
}
}
protected void encodeRequest(Channel channel, ChannelBuffer buffer, Request req) throws IOException {
// 获取序列化扩展实现
Serialization serialization = getSerialization(channel);
// 创建Dubbo协议扩展头字节数组,HEADER_LENGTH=16
// header.
byte[] header = new byte[HEADER_LENGTH];
// 把魔数0xdabb写入协议头
// set magic number.
Bytes.short2bytes(MAGIC, header);
// 设置请求类型与序列化类型,标记到协议头
// set request and serialization flag.
header[2] = (byte) (FLAG_REQUEST | serialization.getContentTypeId());
if (req.isTwoWay()) {
header[2] |= FLAG_TWOWAY;
}
if (req.isEvent()) {
header[2] |= FLAG_EVENT;
}
// 将请求id设置到协议头
// set request id.
Bytes.long2bytes(req.getId(), header, 4);
// 使用serialization将对象数据部分进行编码,并把协议数据部分写入缓存
// encode request data.
int savedWriteIndex = buffer.writerIndex();
buffer.writerIndex(savedWriteIndex + HEADER_LENGTH);
ChannelBufferOutputStream bos = new ChannelBufferOutputStream(buffer);
ObjectOutput out = serialization.serialize(channel.getUrl(), bos);
if (req.isEvent()) {
encodeEventData(channel, out, req.getData());
} else {
encodeRequestData(channel, out, req.getData(), req.getVersion());
}
// 刷新缓存
out.flushBuffer();
if (out instanceof Cleanable) {
((Cleanable) out).cleanup();
}
bos.flush();
bos.close();
// 检查payload(协议数据部分)是否合法
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);
}
protected void encodeResponse(Channel channel, ChannelBuffer buffer, Response res) throws IOException {
int savedWriteIndex = buffer.writerIndex();
try {
// 获取序列化扩展实现
Serialization serialization = getSerialization(channel);
// 创建Dubbo协议扩展头字节数组, HEADER_LENGTH为 16
// 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;
}
// 设置响应类型到第4字节位置
// set response status.
byte status = res.getStatus();
header[3] = status;
// 将请求ID设直到协议头
// set request id.
Bytes.long2bytes(res.getId(), header, 4);
buffer.writerIndex(savedWriteIndex + HEADER_LENGTH);
ChannelBufferOutputStream bos = new ChannelBufferOutputStream(buffer);
// 使用serialization对数据部分进行编码,并把协议数据部分写入缓存
ObjectOutput out = serialization.serialize(channel.getUrl(), bos);
// encode response data or error message.
if (status == Response.OK) {
if (res.isHeartbeat()) {
encodeEventData(channel, out, res.getResult());
} else {
encodeResponseData(channel, out, res.getResult(), res.getVersion());
}
} else {
out.writeUTF(res.getErrorMessage());
}
// 刷新缓存
out.flushBuffer();
if (out instanceof Cleanable) {
((Cleanable) out).cleanup();
}
bos.flush();
bos.close();
int len = bos.writtenBytes();
// 检查payload (协议数据部分)是否合法
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.writerIndex(savedWriteIndex);
// send error message to Consumer, otherwise, Consumer will wait till timeout.
if (!res.isEvent() && res.getStatus() != Response.BAD_RESPONSE) {
Response r = new Response(res.getId(), res.getVersion());
r.setStatus(Response.BAD_RESPONSE);
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);
}
}
}
}
上面的代码其实可以看到dubbo协议的组成,分为2大块:header和data部分。header总包含了16字节,前2字节为魔数,标记一个数据帧的开始,然后是1字节的请求类型和序列化标记id,然后1字节是只在响应报文设置的结果码,然后8字节是请求id,最后4字节是body内容的大小。
数据经过编码后会变成二进制形式的数据,然后通过channel传输到对方去。对方接收到数据后,就会按照上面约定好的数据格式,进行解码。
这里我们看下解码的部分,同时可以从里面学习到对于网络拆包粘包的处理。
我们还是先看看NettyCodecAdapter的InternalDecoder处理,因为InternalDecoder就是解码的内部实现类。
final public class NettyCodecAdapter {
private class InternalDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf input, List<Object> out) throws Exception {
ChannelBuffer message = new NettyBackedChannelBuffer(input);
NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
// decode object.
do {
// 先保存未读取之前的位置
int saveReaderIndex = message.readerIndex();
// 调用DubboCodec对数据进行解码
Object msg = codec.decode(channel, message);
// 如果遇到拆包,则重置message为之前的位置
if (msg == Codec2.DecodeResult.NEED_MORE_INPUT) {
message.readerIndex(saveReaderIndex);
break;
} else {
//is it possible to go here ?
if (saveReaderIndex == message.readerIndex()) {
throw new IOException("Decode without read data.");
}
// 否则,走到这里就是读取成功的
if (msg != null) {
// 把解码成功的对象放入out列表
out.add(msg);
}
}
} while (message.readable());
}
}
}
这里我们可以看到dubbo对于拆包的处理,其实就是判断一下是否遇到了Codec2.DecodeResult.NEED_MORE_INPUT,如果遇到了则放弃前面读取的部分,然后等待下次read通道里面的数据。至于粘包的处理,因为这里读取header的时候,都是按照固定从长度读取,并且读取data的时候,也是按照header里面指定的长度读取的,所以读到的结果一定是完整的,不会出现多余的字节,如果不完整就是走拆包的处理逻辑。
我们接下来继续跟进codec.decode()方法的处理逻辑。
@Override
public Object decode(Channel channel, ChannelBuffer buffer) throws IOException {
int readable = buffer.readableBytes();
byte[] header = new byte[Math.min(readable, HEADER_LENGTH)];
// 先读取协议头
buffer.readBytes(header);
//解析Dubbo协议数据部分
return decode(channel, buffer, readable, header);
}
@Override
protected Object decode(Channel channel, ChannelBuffer buffer, int readable, byte[] header) throws IOException {
// 检查魔数,确认为Dubbo协议帧
// check magic number.
if (readable > 0 && header[0] != MAGIC_HIGH
|| readable > 1 && header[1] != MAGIC_LOW) {
int length = header.length;
if (header.length < readable) {
header = Bytes.copyOf(header, readable);
buffer.readBytes(header, length, readable - length);
}
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);
header = Bytes.copyOf(header, i);
break;
}
}
return super.decode(channel, buffer, readable, header);
}
// 检查是否读取了一个完整的Dubbo协议头
// check length.
if (readable < HEADER_LENGTH) {
return DecodeResult.NEED_MORE_INPUT;
}
// 从协议头的最后四个字节读取协议数据部分的大小
// get data length.
int len = Bytes.bytes2int(header, 12);
checkPayload(channel, len);
// 如采遇到半包问题,则直接返回
int tt = len + HEADER_LENGTH;
if (readable < tt) {
return DecodeResult.NEED_MORE_INPUT;
}
// 解析协议数据部分
// limit input stream.
ChannelBufferInputStream is = new ChannelBufferInputStream(buffer, len);
try {
// 这里调用的是子类DubboCodec
return decodeBody(channel, is, header);
} 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);
}
}
}
}
所以我们继续跟进DubboCodec.decodeBody()方法。
public class DubboCodec extends ExchangeCodec {
@Override
protected Object decodeBody(Channel channel, InputStream is, byte[] header) throws IOException {
// 解析请求类型和消费端序列化的类型
byte flag = header[2], proto = (byte) (flag & SERIALIZATION_MASK);
// get request id.
// 解析请求id
long id = Bytes.bytes2long(header, 4);
// 解码响应类型
if ((flag & FLAG_REQUEST) == 0) {
// decode response.
Response res = new Response(id);
// 事件类型
if ((flag & FLAG_EVENT) != 0) {
res.setEvent(true);
}
// 响应码
// get status.
byte status = header[3];
res.setStatus(status);
try {
// 如果响应是成功的
if (status == Response.OK) {
Object data;
// 如果是事件类型
if (res.isEvent()) {
ObjectInput in = CodecSupport.deserialize(channel.getUrl(), is, proto);
data = decodeEventData(channel, in);
}
// 如果是RPC响应
else {
// DecodeableRpcResult会根据接口方法的返回值类型,将数据转换为返回值类型的对象
DecodeableRpcResult result;
if (channel.getUrl().getParameter(DECODE_IN_IO_THREAD_KEY, DEFAULT_DECODE_IN_IO_THREAD)) {
result = new DecodeableRpcResult(channel, res, is,
(Invocation) getRequestData(id), proto);
result.decode();
}
// 默认走这里,然后通过DecodeHandler的包装,最后调用DecodeableRpcResult的decode()处理
else {
result = new DecodeableRpcResult(channel, res,
new UnsafeByteArrayInputStream(readMessageData(is)),
(Invocation) getRequestData(id), proto);
}
data = result;
}
res.setResult(data);
} else {
ObjectInput in = CodecSupport.deserialize(channel.getUrl(), is, proto);
res.setErrorMessage(in.readUTF());
}
} catch (Throwable t) {
if (log.isWarnEnabled()) {
log.warn("Decode response failed: " + t.getMessage(), t);
}
res.setStatus(Response.CLIENT_ERROR);
res.setErrorMessage(StringUtils.toString(t));
}
return res;
}
// 类型为请求类型
else {
// decode request.
Request req = new Request(id);
req.setVersion(Version.getProtocolVersion());
req.setTwoWay((flag & FLAG_TWOWAY) != 0);
if ((flag & FLAG_EVENT) != 0) {
req.setEvent(true);
}
try {
Object data;
// 如果是事件类型
if (req.isEvent()) {
ObjectInput in = CodecSupport.deserialize(channel.getUrl(), is, proto);
data = decodeEventData(channel, in);
}
// 如果是RPC请求
else {
// DecodeableRpcInvocation会将数据转换为RpcInvocation对象
DecodeableRpcInvocation inv;
if (channel.getUrl().getParameter(DECODE_IN_IO_THREAD_KEY, DEFAULT_DECODE_IN_IO_THREAD)) {
inv = new DecodeableRpcInvocation(channel, req, is, proto);
inv.decode();
}
// 默认走这里,然后通过DecodeHandler的包装,最后调用DecodeableRpcInvocation的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;
}
}
}
可以看到,如果是响应数据,则会在最后调用DecodeableRpcResult.decode()进行数据的解码,这个跟第13篇的最后说是,“invoker.invoke()返回的是前面返回的DecodeableRpcResult对象”,就串联起来了。如果是请求数据,则是调用DecodeableRpcInvocation.decode()进行数据的解码。