Thrift底层对java传输空值对象的处理

50 阅读4分钟

一、问题背景

基本需求

对于模型租户控制台新增了一个字段,名称为max_token_limit,表示模型请求的最大token限制,现在需要开放一个RPC接口,用来查询某个模型的最大token的属性,给其他模块调用。RPC框架采用的是Thrift,它通过IDL具备跨语言调用的能力,支持语言包括但不限于java、golang、c++、python等。

研发过程

功能开发过程并不难,根据传入的模型信息参数,由于支持批量传输,所以入参是一个列表,我们只需要从数据库里面批量读取当前模型允许的最大token属性,存储到一个list里面,然后传输给调用方就行。在线下boe环境数据库里面,我批量设置了max_token_limit初始数值为0,在预发和线上环境的数据库里面则没有进行初始化设置,默认为null.一番简单改代码后,线下boe环境测试成功。

预发环境出现的问题

当我把代码发到预发上以后,控制台出现报错空指针异常,获取logID,通过日志系统进行链路流程追踪,发现报错信息如下。 可以发现是运行到Thrift的某个write函数的地方出现了错误,但是具体细节还看不出来。

在这里插入图片描述


二、问题排查分析

接下来我们找到对应的代码行数,找到日志提示出错的代码行数,查看Thrfit底层api的代码如下。

在这里插入图片描述

出错的位置是在for循环这个位置,报出了空指针异常。所以我们可以看到对于从数据库批量读取到的maxTokenSizeList的遍历时候,Thrfit利用long类型来接收模型参数列表里面的每一个参数。所以我们猜测由于数据库里面maxTokenSize这个属性初始值为null,所以list里面全部都是null对象,导致java语言对象转基本long类型的时候报错出现了空指针异常。

三、Thrift底层解析

那么为什么不可以用对象类型去接收数据库默认的null对象呢?其实这个涉及到Thrfit底层的数据类型特点。可以参考下面链接的内容: www.python100.com/html/2G44YS… 在这里插入图片描述 不难发现,Thrfit底层只支持基本数据类型,并不包括他们对应的包装类,也就是long是没有对应Long的包装的,但是java语言里面是有的。者就会出现java API层面上的null转long的基本数据类型问题。

所以扩展一下,java里面我们定义一个类的一些属性的时候,可以尽量用包装类接收,比如int改成Integer,long改成Long,因为int和long默认的值是0,某些情况下,0可能有特殊意义,改成包装类来接收null可以避免一些潜在的问题。

再扩展一下,java里面是有枚举类Enum的,但是go语言是没有的,所以当他们两种语言实现同一个Thrift的RPC接口并且各自读取返回进行对比时候,就会出现兼容性的问题,其实go语言对于java层面的枚举类型对象接收时候,只会获取后面对应的值,所以最好用String类型来替代。 在这里插入图片描述

四、最终处理

由于这个max_Token_Limit属性是新加的,没配置默认值为null,后续上线以后我们都会改成对应的值,所以其实可以在后端做兼容性处理,当我们读起到null的时候,我们直接改传输一个0,这样可以避免Thrfit底层API对于空值转换的时候出现空指针异常。

总结

由于Thrfit只支持基本数据类型而没有对应的包装类,所以导致java读取到数据库默认的空值以后传输的空值对象无法转换为基本数据类型。所以采取后端兼容性的处理方式,如果读取到的数据库默认值为空,用java先拆箱直接对应传输一个基本数据类型,后续再通过编辑接口设置成对应的数值。