11 dubbo源码学习_dubbo协议通信

dubbo在通信上也支持非常多的网络协议,而dubbo协议属于dubbo框架自研,整体协议也比较有代表性,采用定长协议头+变长协议休的形式;

1. dubbo协议格式

在这里插入图片描述

  • Magic - Magic High & Magic Low (16 bits)

    标识协议版本号,Dubbo 协议:0xdabb

  • Req/Res (1 bit)

    标识是请求或响应。请求: 1; 响应: 0。

  • 2 Way (1 bit)

    仅在 Req/Res 为1(请求)时才有用,标记是否期望从服务器返回值。如果需要来自服务器的返回值,则设置为1。

  • Event (1 bit)

    标识是否是事件消息,例如,心跳事件。如果这是一个事件,则设置为1。

  • Serialization ID (5 bit)

    标识序列化类型:比如 fastjson 的值为6。

  • Status (8 bits)

    仅在 Req/Res 为0(响应)时有用,用于标识响应的状态。

    • 20 - OK
    • 30 - CLIENT_TIMEOUT
    • 31 - SERVER_TIMEOUT
    • 40 - BAD_REQUEST
    • 50 - BAD_RESPONSE
    • 60 - SERVICE_NOT_FOUND
    • 70 - SERVICE_ERROR
    • 80 - SERVER_ERROR
    • 90 - CLIENT_ERROR
    • 100 - SERVER_THREADPOOL_EXHAUSTED_ERROR
  • Request ID (64 bits)

    标识唯一请求。类型为long。

  • Data Length (32 bits)

    序列化后的内容长度(可变部分),按字节计数。int类型。

  • Variable Part

    被特定的序列化类型(由序列化 ID 标识)序列化后,每个部分都是一个 byte [] 或者 byte

    • 如果是请求包 ( Req/Res = 1),则每个部分依次为:
      • Dubbo version
      • Service name
      • Service version
      • Method name
      • Method parameter types
      • Method arguments
      • Attachments
    • 如果是响应包(Req/Res = 0),则每个部分依次为:
      • 返回值类型(byte),标识从服务器端返回的值类型:
        • 返回空值:RESPONSE_NULL_VALUE 2
        • 正常响应值: RESPONSE_VALUE 1
        • 异常:RESPONSE_WITH_EXCEPTION 0
      • 返回值:从服务端返回的响应bytes

2. 消费端网络请求源码分析

org.apache.dubbo.remoting.Codec2定义为I/O的 Codec 过程,因此主要的方法就是encode和decode;
网络发送的源码入口在:com.alibaba.dubbo.remoting.transport.netty4.NettyChannel#send
在这里插入图片描述
这是一个调用链路,从inovker到网络请求的具体流程;

final NettyClientHandler nettyClientHandler = new NettyClientHandler(getUrl(), this);
bootstrap.handler(new ChannelInitializer() {
   
    @Override
    protected void initChannel(Channel ch) throws Exception {
   
        NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this);
        ch.pipeline()//.addLast("logging",new LoggingHandler(LogLevel.INFO))//for debug
                .addLast("decoder", adapter.getDecoder())
                .addLast("encoder", adapter.getEncoder())
                .addLast("handler", nettyClientHandler);
        String socksProxyHost = ConfigUtils.getProperty(SOCKS_PROXY_HOST);
        if(socksProxyHost != null) {
   
            int socksProxyPort = Integer.parseInt(ConfigUtils.getProperty(SOCKS_PROXY_PORT, DEFAULT_SOCKS_PROXY_PORT));
            Socks5ProxyHandler socks5ProxyHandler = new Socks5ProxyHandler(new InetSocketAddress(socksProxyHost, socksProxyPort));
            ch.pipeline().addFirst(socks5ProxyHandler);
        }
    }
});

对于消费端,也就是网络out的流程:nettyClientHandler->encoder;调用nettyClientHandler处理业务逻辑,然后调用encoder进行编码,将编码的结果传输到服务提供者中;
adapter.getEncoder()会调用到:

// codec2的获取逻辑,默认获取telnet,而Codec2有dubbo协议的实现
protected static Codec2 getChannelCodec(URL url) {
   
    String codecName = url.getParameter(Constants.CODEC_KEY, "telnet");
    if (ExtensionLoader.getExtensionLoader(Codec2.class).hasExtension(codecName)) {
   
        return ExtensionLoader.getExtensionLoader(Codec2.class).getExtension(codecName);
    } else {
   
        return new CodecAdapter(ExtensionLoader.getExtensionLoader(Codec.class)
                .getExtension(codecName));
    }
}
private class InternalEncoder extends MessageToByteEncoder {
   

    @Override
    protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
   
        com.alibaba.dubbo.remoting.buffer.ChannelBuffer buffer = new NettyBackedChannelBuffer(out);
        Channel ch = ctx.channel();
        NettyChannel channel = NettyChannel.getOrAddChannel(ch, url, handler);
        try {
   
        	// 所以这里的codec获取就是dubboCodec
            codec.encode(channel, buffer, msg);
        } finally {
   
            NettyChannel.removeChannelIfDisconnected(ch);
        }
    }
}

dubboCodec.encode
dubboCodec的encode由父类ExchangeCodec实现:

@Override
public void encode(Channel channel, ChannelBuffer buffer, Object msg) throws IOException {
   
	// 它会根据msg类型判断是对请求还是响应编码;
    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

protected void encodeRequest(Channel channel, ChannelBuffer buffer, Request req) throws IOException {
   
    // 获取序列化框架,默认为Hessian
    Serialization serialization = getSerialization(channel);
    // header.
    // HEADER_LENGTH=16字节
    byte[] header = new byte[HEADER_LENGTH];
    // set magic number.
    // 写入固定的魔数;
    Bytes.short2bytes(MAGIC, header);

    // set request and serialization flag.
    // 设置序列化id号
    header[2] = (byte) (FLAG_REQUEST | serialization.getContentTypeId());
    // 设置是否期望返回标识
    if (req.isTwoWay()) header[2] |= FLAG_TWOWAY;
    // 设置事件类型
    if (req.isEvent()) header[2] |= FLAG_EVENT;

    // 设置请求id
    Bytes.long2bytes(req.getId(), header, 4);

    // encode request data.
    // 记录buffer中设置header的位置;
    int savedWriteIndex = buffer.writerIndex();
    // 预留出header的长度;
    buffer.writerIndex(savedWriteIndex + HEADER_LENGTH);
    ChannelBufferOutputStream bos = new ChannelBufferOutputStream(buffer);
    // 将bos设置到out中;
    ObjectOutput out = serialization.serialize(channel.getUrl(), bos);
    if (req.isEvent()) {
   
        encodeEventData(channel, out, req.getData());
    } else {
   
        // 向output中写入body
        encodeRequestData(channel, out, req.getData(), req.getVersion());
    }
    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
    // 设置writerIndex索引位置,将header设置到这里;
    buffer.writerIndex(savedWriteIndex);
    buffer.writeBytes(header); // write header.
    // 设置writerIndex的新位置;
    buffer.writerIndex(savedWriteIndex + HEADER_LENGTH + len);
}

// DubboCodec
@Override
protected void encodeRequestData(Channel channel, ObjectOutput out, Object data, String version) throws IOException {
   
    RpcInvocation inv = (RpcInvocation) data;

    out.writeUTF(version);
    // path
    out.writeUTF(inv.getAttachment(Constants.PATH_KEY
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值