motan源码解读之-- motan通信协议分析

原创
2016/07/04 12:35
阅读数 1.3K

Motan RPC是基于Netty实现服务的调用。实现过程很简单明了。

基本流程如下: NettyClient构建Request对Request进行编码,发送到目标NettyServer; 目标NettyServer接收到编码后的Request进行解码,还原Request,再Invoke到本地服务,对调用的返回结果Response进行编码,NettyClient接收到编码后的Response进行解码还原结果。 流程示意图:

输入图片说明

先不考虑Netty之间是如何传输的,本文重点是分析是Motan协议如何实现编码解密。

Request和Response

Request和Response实现了对RPC调用的请求参数和返回结果的封装。

Request构成:核心参数RequestId+接口类+方法+方法参数+方法参数实例,这也是我们正常调用一个接口方法的基本参数构成。

package com.weibo.api.motan.rpc;

import java.util.Map;

public interface Request {

    /**
     * 
     * 接口类包的完整路径
     * 
     * @return
     */
    String getInterfaceName();

    /**
     * 调用接口方法
     * 
     * @return
     */
    String getMethodName();

    /**
     * 调用接口参数描述
     * 
     * @return
     */
    String getParamtersDesc();

    /**
     * 调用接口参数实例
     * 
     * @return
     */
    Object[] getArguments();

    /**
     * 框架参数
     * 
     * @return
     */
    Map<String, String> getAttachments();

 
    /**
     * request id
     * 
     * @return
     */
    long getRequestId();

    /**
     * 重试次数
     * 
     * @return
     */
    int getRetries();

    /**
     *rpc协议版本
     */
    byte getRpcProtocolVersion();
}

Response构成: 需要处理情况有1正常返回、返回空值、异常返回,相比Request复杂一点。

package com.weibo.api.motan.rpc;

import java.util.Map;

public interface Response {

    /**
     * <pre>
     *         如果 request 正常处理,那么会返回 Object value,而如果 request 处理有异常,那么 getValue 会抛出异常
     * </pre>
     * 
     * @throws RuntimeException
     * @return
     */
    Object getValue();

    /**
     * 如果request处理有异常,那么调用该方法return exception 如果request还没处理完或者request处理正常,那么return null
     * @return
     */
    Exception getException();

    /**
     * 与 Request 的 requestId 相对应
     * 
     * @return
     */
    long getRequestId();

    /**
     * 业务处理时间
     * 
     * @return
     */
    long getProcessTime();
    
    /**
    *超时时间
    **/
    int getTimeout();

/**
    *framework 参数
    **/
    Map<String, String> getAttachments();

    // 获取rpc协议版本,可以依据协议版本做返回值兼容
    void setRpcProtocolVersion(byte rpcProtocolVersion);

    byte getRpcProtocolVersion();
}

编码

Motan对Request和Response采用了统一的编码方式;统一在com.weibo.api.motan.protocol.rpc.DefaultRpcCodec中实现。 Motan通信数据由2部分组成: Header:数据头,16字节; Body:数据体,变长。

Header构造(依次写入): Header部分都是定长的数据类型,按short、byte、int、long等java类型写入。

2字节:Magic魔术字常量,标名这是motan协议,(short) 0xF0F0;

1字节:VERSION_1基本版本,VERSION_2数据包压缩版本,压缩版的需要对body压缩解压处理;

1字节:flag标记类型;0:request,1:response及其他response;

    public static final byte FLAG_REQUEST = 0x00;
    public static final byte FLAG_RESPONSE = 0x01;
    public static final byte FLAG_RESPONSE_VOID = 0x03;
    public static final byte FLAG_RESPONSE_EXCEPTION = 0x05;
    public static final byte FLAG_RESPONSE_ATTACHMENT = 0x07;
    public static final byte FLAG_OTHER = (byte) 0xFF;

8字节:long型,requestId,业务流水ID;

4字节:body数据体长度

Body构成: Body部分根据类型分别处理,通过ObjectOutput依次写入返回byte[]:

Request类型:

 output.writeUTF(request.getInterfaceName());
 output.writeUTF(request.getMethodName());
 output.writeUTF(request.getParamtersDesc());

将接口参数实例序列化后写入

output.writeObject(serialize.serialize(message));//写序列化对象

依次写入框架参数,如果空则写入0;

Response类型: 根据返回结果进行写入。

        output.writeLong(value.getProcessTime());

        if (value.getException() != null) {//异常处理
            output.writeUTF(value.getException().getClass().getName());
            serialize(output, value.getException(), serialization);
            flag = MotanConstants.FLAG_RESPONSE_EXCEPTION;
        } else if (value.getValue() == null) {//空值处理
            flag = MotanConstants.FLAG_RESPONSE_VOID;
        } else {//默认处理
            output.writeUTF(value.getValue().getClass().getName());
            serialize(output, value.getValue(), serialization);
            flag = MotanConstants.FLAG_RESPONSE;
        }

解码

解码就是对编码过程的逆向解析,先解析头数据,根据头里面的数据体长度读取数据体;再根据flag判断解码Request还是Response对象。

整体上来说,Motan协议还是很简单,当然大部分的通信协议都是基于这样的方式处理。

Motan还实现了基于GZIP实现协议数据体压缩,当然,你也可以基于SPI规则自己扩展Snappy、LZ4等压缩方式;

Motan协议没有去实现请求版本管理,CRC校验,可能都是基于内部跑,没必要控制吧。

展开阅读全文
打赏
0
1 收藏
分享
加载中
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部