2021年7月

背景:

由于 历史原因 ,uid 定义为 int ,现在由于 业务扩展 ,int已经不能满足 了,需要将int 转给 long
并且内部接口是通过thrift + TBinaryProtocol 来进行通讯的

可以直接将thrift定义从int 转成 long的情况

  1. 定义为单值的类型

    struct user {
        1:i32 userId
    }
    

    这种是可以直接改成

    struct user {
        1:i64 userId
    }
    
  2. 单值的参数

    ret getUserInfo(1:i32 userId)
    

    这种是可以直接改成

    ret getUserInfo(1:i64 userId)
    

不可以直接将thrift定义从int 转成 long的情况

  1. 定义为多值的类型

    struct user {
        1:list<i32> userIdList // 这里会有兼容问题
        2:set<i32> userIdSet // 这里会也有兼容问题
    }
    
  2. 定义为多值的参数

    ret getUserInfo(
        1:list<i32> userIdList, // 这里会有兼容问题
        2:set<i32> userIdSet // 这里会也有兼容问题
    )
    

客户端跟服务端定义不一致,会导致的服务端异常

Caused by: org.apache.thrift.transport.TTransportException: null
        at org.apache.thrift.transport.TIOStreamTransport.read(TIOStreamTransport.java:132) ~[libthrift-0.9.2.jar:0.9.2]
        at org.apache.thrift.transport.TTransport.readAll(TTransport.java:86) ~[libthrift-0.9.2.jar:0.9.2]
        at org.apache.thrift.protocol.TBinaryProtocol.readAll(TBinaryProtocol.java:429) ~[libthrift-0.9.2.jar:0.9.2]
        at org.apache.thrift.protocol.TBinaryProtocol.readI64(TBinaryProtocol.java:337) ~[libthrift-0.9.2.jar:0.9.2]

原因

单值参数 或者 单值属性不会有问题是因为thrift 做了容错处理, 核心代码 如下

       case 1: // USER_ID
              if (schemeField.type == org.apache.thrift.protocol.TType.I64) {  // 这里会进行schemeField.type 的校验
                struct.userId = iprot.readI64();
                struct.setUserIdIsSet(true);
              } else { // 类型 跟 定义的不一致 根据传过来的 类型进行处理
                org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
              }
              break;

skip 部分代码

    switch (type) {
      case TType.BOOL:
        prot.readBool();
        break;

      case TType.BYTE:
        prot.readByte();
        break;

      case TType.I16:
        prot.readI16();
        break;

      case TType.I32:
        prot.readI32();
        break;

      case TType.I64:
        prot.readI64();
        break;

多值会有问题(list 类似):

       if (schemeField.type == org.apache.thrift.protocol.TType.SET) {
                {
                  org.apache.thrift.protocol.TSet _set0 = iprot.readSetBegin();
                  struct.playerIdSet = new HashSet<Long>(2*_set0.size);
                  long _elem1;
                  for (int _i2 = 0; _i2 < _set0.size; ++_i2)
                  {
                    _elem1 = iprot.readI64(); // 元素是没有进行类型判断的,long类型会读取8位,int只会读取6位
                    struct.playerIdSet.add(_elem1);
                  }
                  iprot.readSetEnd();
                }
                struct.setPlayerIdSetIsSet(true);
              } else { 
                org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
              }