首页 > 编程知识 正文

rpc接口怎么写,rpc接口文档

时间:2023-05-04 19:39:32 阅读:12408 作者:1645

过去的热门文章:

《往期精选优秀博文都在这里了!》 2,老板,服务层需要接口吗? 3、强烈建议经常写“烂码”的学生使用这个IDEA插件! 4、烂街上的Spring循环依赖该怎么回答? 5、IDEA 2020年最后一个版本更新,机器学习全部成为作者l Hollis

源Lhollis(id:HollisChuang ) ) )。

最近,我们的在线环境出现了问题。 在线代码在运行时抛出了IllegalArgumentException。 分析堆栈后,最根本的异常如下。

ava.lang.illegalargumentexception : noenumconstantcom.a.b.f.m.a.c.atype.p _ m可能是以上内容,看起来还是很简单的。 提示的错误信息在Atype中

故障诊断结果显示,在线出现此异常之前,已发布了APP应用程序所依赖的下游系统,并且在发布过程中对API软件包进行了更改。 主要更改是在RPC接口的Response返回值类的枚举参数AType中添加了一个名为P_M的枚举项。

但是,下游系统发布的时候,没有通知我们负责的这个系统要升级,所以被报告错了。

让我们分析一下为什么会发生这种事。

问题重现

首先,下游系统a提供的二进制库之一接口的返回值具有枚举类型的参数。

一个库是指本项目中的依赖

二方库是指公司内部其他项目提供的依赖

三方库是指来自其他组织、公司等第三方的依赖

publicinterfaceafacadeservice { publicaresponsedosth (arequestarequest ); 公共类响应{私有类booleansuccess; 私有类型; }publicenumAType{P_T,A_B}然后b系统依赖于这两个库,在RPC远程调用中调用AFacadeService的doSth方法。

publicclassbservice { @ autowiredafacadeserviceafacadeservice; 公共语音dosth () {ARequestaRequest=newARequest; aresponse aresponse=afacadeservice.dosth (a request; ATypeaType=aResponse.getAType (; }此时,如果a和b系统依赖于同一双库,则两者使用的枚举AType属于同一类,其中的枚举项也匹配。 这种情况没有问题。

但是,如果有一天这两者库升级了,并且向名为AType的枚举类中添加了新的枚举项P_M,则只有系统a被升级了,但系统b没有被升级。

那么,a系统依赖的AType是这样的:

publicenumAType{P_T,A_B,P_M}和b系统依赖的AType如下所示:

publicenumAType{P_T,A_B}在此情况下为,在B系统通过RPC调用A系统的时候,如果A系统返回的AResponse中的aType的类型为新增的P_M时候,B系统就会无法解析。一般在这种时候,RPC框架就会发生反序列化异常。导致程序被中断。

原理分析

这个问题的现象被清楚地分析了。 那么,让我们来看看原理是怎么回事,为什么会发生这样的异常。

其实这个原理也不难。 这样的RPC框架大多数会采用JSON的格式进行数据传输(即客户端)将返回值序列化为JSON字符串,然后服务端再将JSON字符串反序列化为Java对象。

另一方面,在反序列化过程中,JSON尝试通过调用相应枚举类的valueOf方法获取枚举类型的相应枚举。

查看枚举类的valueOf方法实现,可以看到如果从枚举类中找不到对应的枚举项的时候,就会抛出IllegalArgumentException

publicstaticTextendsEnumTT

 valueOf(Class<T> enumType, String name) {    T result = enumType.enumConstantDirectory().get(name);    if (result != null)        return result;    if (name == null)        throw new NullPointerException("Name is null");    throw new IllegalArgumentException(        "No enum constant " + enumType.getCanonicalName() + "." + name);}

关于这个问题,其实在《阿里巴巴Java开发手册》中也有类似的约定:

这里面规定"对于二方库的参数可以使用枚举,但是返回值不允许使用枚举"。这背后的思考就是本文上面提到的内容。

扩展思考

为什么参数中可以有枚举?

不知道大家有没有想过这个问题,其实这个就和二方库的职责有点关系了。

一般情况下,A系统想要提供一个远程接口给别人调用的时候,就会定义一个二方库,告诉其调用方如何构造参数,调用哪个接口。

而这个二方库的调用方会根据其中定义的内容来进行调用。而参数的构造过程是由B系统完成的,如果B系统使用到的是一个旧的二方库,使用到的枚举自然是已有的一些,新增的就不会被用到,所以这样也不会出现问题。

比如前面的例子,B系统在调用A系统的时候,构造参数的时候使用到AType的时候就只有P_T和A_B两个选项,虽然A系统已经支持P_M了,但是B系统并没有使用到。

如果B系统想要使用P_M,那么就需要对该二方库进行升级。

但是,返回值就不一样了,返回值并不受客户端控制,服务端返回什么内容是根据他自己依赖的二方库决定的。

但是,其实相比较于手册中的规定,我更加倾向于,在RPC的接口中入参和出参都不要使用枚举。

一般,我们要使用枚举都是有几个考虑:

1、枚举严格控制下游系统的传入内容,避免非法字符。

2、方便下游系统知道都可以传哪些值,不容易出错。

不可否认,使用枚举确实有一些好处,但是我不建议使用主要有以下原因:

1、如果二方库升级,并且删除了一个枚举中的部分枚举项,那么入参中使用枚举也会出现问题,调用方将无法识别该枚举项。

2、有的时候,上下游系统有多个,如C系统通过B系统间接调用A系统,A系统的参数是由C系统传过来的,B系统只是做了一个参数的转换与组装。这种情况下,一旦A系统的二方库升级,那么B和C都要同时升级,任何一个不升级都将无法兼容。

我其实建议大家在接口中使用字符串代替枚举,相比较于枚举这种强类型,字符串算是一种弱类型。

如果使用字符串代替RPC接口中的枚举,那么就可以避免上面我们提到的两个问题,上游系统只需要传递字符串就行了,而具体的值的合法性,只需要在A系统内自己进行校验就可以了。

为了方便调用者使用,可以使用javadoc的@see注解表明这个字符串字段的取值从那个枚举中获取。

public Class AResponse{    private Boolean success;    /**    *  @see AType     */    private String aType;}

对于像阿里这种比较庞大的互联网公司,随便提供出去的一个接口,可能有上百个调用方,而接口升级也是常态,我们根本做不到每次二方库升级之后要求所有调用者跟着一起升级,这是完全不现实的,并且对于有些调用者来说,他用不到新特性,完全没必要做升级。

还有一种看起来比较特殊,但是实际上比较常见的情况,就是有的时候一个接口的声明在A包中,而一些枚举常量定义在B包中,比较常见的就是阿里的交易相关的信息,订单分很多层次,每次引入一个包的同时都需要引入几十个包。

对于调用者来说,我肯定是不希望我的系统引入太多的依赖的,一方面依赖多了会导致应用的编译过程很慢,并且很容易出现依赖冲突问题。

所以,在调用下游接口的时候,如果参数中字段的类型是枚举的话,那我没办法,必须得依赖他的二方库。但是如果不是枚举,只是一个字符串,那我就可以选择不依赖。

所以,我们在定义接口的时候,会尽量避免使用枚举这种强类型。规范中规定在返回值中不允许使用,而我自己要求更高,就是即使在接口的入参中我也很少使用。

最后,我只是不建议在对外提供的接口的出入参中使用枚举,并不是说彻底不要用枚举,我之前很多文章也提到过,枚举有很多好处,我在代码中也经常使用。所以,切不可因噎废食。

当然,文中的观点仅代表我个人,具体是是不是适用其他人,其他场景或者其他公司的实践,需要读者们自行分辨下,建议大家在使用的时候可以多思考一下。

往期热门文章:1、《历史文章分类导读列表!精选优秀博文都在这里了!》2、万亿级数据应该怎么迁移?3、从应用到底层 36张图带你进入Redis世界4、写代码有这16个好习惯,可以减少80%非业务的bug5、顺丰快递:请签收MySQL灵魂十连6、一个基于SpringBoot + MyBatis + Vue的代码生成器7、Redis 分布式锁使用不当,超卖了100瓶飞天茅台!!!8、如何设计订单系统?这篇写得太好了!9、如果MySQL磁盘满了,会发生什么?还真被我遇到了!10、阿里开源的27个项目,值得收藏!‍

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。