dubbo源码第15篇 dubbo协议的编解码

dubbo协议的编码

我们先回顾一下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传输到对方去。对方接收到数据后,就会按照上面约定好的数据格式,进行解码。

dubbo协议的解码

这里我们看下解码的部分,同时可以从里面学习到对于网络拆包粘包的处理。
我们还是先看看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()进行数据的解码。

你可能感兴趣的:(一口气读完dubbo核心源码)