RPC结构泛化调用功用在网关、接口测验等场景下有着广泛的需求,本文给各位读者介绍一下干流的泛化调用完成办法及原理,比较各种完成计划的优缺陷,并共享泛化调用在转转的实践。一方面有助于RPC结构运用方了解泛化调用,更好地运用泛化调用;另一方面关于有自研RPC结构需求的开发者在挑选泛化调用完成计划上有一定参阅意义。
1 一般RPC调用
根据动态署理技能,RPC结构客户端做到了调用RPC办法与调用本地办法相同的体会。一般情况下服务端定义服务接口,并将接口打包到二方jar包发布。服务端在服务进程中完成该接口,而调用方在进程中根据该接口创立动态署理进行调用,与调用本地办法体会一起。
例如有接口HelloService
,被打包在demo-service-interfaces.jar
包中。
public interface HelloService {
String hello(String name);
}
服务端依靠demo-service-interfaces.jar
,创立HelloServiceImpl
完成该接口。
public class HelloServiceImpl implements HelloService {
@Override
public String hello(String name) {
return "hello, " + name;
}
}
客户端相同依靠demo-service-interface.jar
,创立HelloService
的署理类,以下为代码示例,实际上创立署理类,发送接口、参数,接收返回结果等操作都是封装在结构内的。
HelloService helloService = (HelloService)Proxy.newProxyInstance(this.getClass().getClassLoader(), HelloService.class, (InvocationHandler) (proxy, method, args) -> {
//都是封装在结构内的
//获取办法、参数类型
String methodName = method.getName();
Class<?>[] parameterTypes = method.getParameterTypes();
//发送办法、参数类型和实参到服务端并返回结果
return request(methodName, parameterTypes, args);
});
String result = helloService.hello("jack");
System.out.println(result);
2 网关、接口测验等场景下的需求
由上文能够看到一般的RPC调用需求将接口类(参数和返回值如果是POJO类型相同需求一同打包)打到一个jar包中,被服务方和调用方一起依靠。这种办法在多大数事务场景中是适用的,且愈加便利,由于所依靠的接口jar包是可枚举的。
可是在一些特殊的场景下依靠接口jar包变得很不便利,比如网关、接口测验渠道等。例如运用http网关署理私有协议RPC请求,如果在网关中依靠接口jar包,那么在新增办法或许接口时网关需求从头编译上线。而接口测验渠道需求对全公司所有的RPC接口进行测验,将全公司所有的接口jar包添加到测验渠道的依靠中显然是不可行的。
在这些场景下就诞生了对泛化调用的需求。
3 泛化调用
泛化调用便是在不依靠服务方接口jar包的情况下进行调用,包括对调用办法的泛化、参数的泛化和返回值的泛化。
public interface GenericService {
Object $genericInvoke(String methodName, String[] parameterTypes, Object[] args);
}
在没有接口类依靠的情况下,parameterTypes
需求经过字符串指定,而args
和返回值如果是jdk
内置类型的话与一般调用无异,而如果是POJO
类型的话则需求寻找一种通用的表明办法。
下一般RPC调用的序列化与反序列化原理,如下图所示,实际上序列化结构在将POJO
序列化成字节数组之前需求解析POJO
的类结构生成序列化中间体,当然序列化中间体并非一定能在序列化结构中找到对应的类,有时候这个中间体是虚拟的。
3.1 根据Java Bean的泛化调用
根据Java Bean
的泛化调用是经过一致的Java Bean
描绘符(JavaBeanDescriptor
)来描绘POJO
目标,它作业在序列化层之上,例如dubbo
支撑该种类型的泛化调用,在运用泛化调用时,直接传递JavaBeanDescriptor
目标作为参数,基本原理如下图所示。
该泛化调用的完成通用性比较强,与底层序列化无关,可是复杂度较高,需求RPC结构处理POJO
和JavaBeanDescriptor
之间的转化。
3.2 根据序列化中间体的泛化调用
支撑根据序列化中间体的泛化调用的RPC结构典型的如sofa-rpc
,运用了sofa-hessian
序列化结构,sofa-hessian
是在hessian
序列化结构基础上进行二次开发的,笼统出了序列化中间体,如GenericObject
、GenericMap
、GenericArray
等。
转转RPC结构在支撑泛化调用时也参阅了sofa-hessian
的完成,对hessian
序列化结构进行二次开发,而且有所改进。
而json
序列化天然具备序列化中间体,即JsonObject
或许json String
,在运用json
序列化时调用方能够直接将Json Object
或许json String
作为参数替代POJO
进行调用。转转RPC结构也支撑根据json
序列化的泛化调用。
dubbo
除了支撑根据Java Bean
的泛化调用,还支撑json-protobuf
泛化调用,也便是说调用方能够运用json
描绘protobuf
目标,在反序列化时能够将json
反序列为protobuf
目标再转化成POJO
,而这些功用自身是序列化结构所供给,不需求RPC结构做额外的开发支撑。
根据序列化中间体的泛化调用与根据Java Bean
的泛化调用相比,完成较为简略,有些序列化结构自身原生就支撑,或许对序列化结构做简略的二次开发即可完成,缺陷是与序列化结构耦合。
4 泛化调用在转转的实践
现在泛化调用在转转公司运用最广泛的范畴便是接口测验,咱们供给了一致的测验API渠道。经过该渠道能够运用http + json的办法完成对恣意服务、恣意节点、恣意办法的调用,而测验API渠道不需求依靠任何服务的接口jar包。而且API渠道也没有依靠RPC结构jar包,由于转转RPC结构完成了在同一个端口上一起兼容私有的二进制协议及公有的http协议,也便是说能够运用http请求来建议RPC调用。
一起还支撑获取恣意服务、恣意节点、恣意办法参数及返回值的JsonSchema
,如下代码所示。
{
"msg": "success",
"data": {
"schema": {
"returnValue": {
"type": "array",
"items": {
"type": "object",
"id": "urn:jsonschema:com:bj58:zhuanzhuan:arch:user:atomic:entity:User",
"properties": {
"id": {
"type": "string"
},
"userName": {
"type": "string"
},
"userNamePinyin": {
"type": "string"
},
"mock": {
"type": "boolean"
}
}
}
},
"parameters": {
"pageNum": {
"type": "integer"
},
"pageSize": {
"type": "integer"
}
}
}
},
"code": 0
}
未来转转的网关也将根据泛化调用进行开发。
5 总结
RPC结构的泛化调用在网关、测验渠道等范畴运用广泛,现在干流的泛化调用完成有根据Java Bean
规范的泛化调用和根据序列化中间体的泛化调用,它们的优缺陷分别如下:
-
根据
Java Bean
的泛化调用:优点是与序列化无关;缺陷是RPC结构需求完成JavaBeanDescriptor
向POJO
的转化功用,较为复杂。 - 根据序列化中间体的泛化调用:优点是RPC结构完成简略,序列化结构原生支撑或许仅需少数改造;缺陷是与特定的序列化结构耦合。
在开发RPC结构时,具体挑选哪种泛化调用完成办法,还需求结合实际情况做出挑选。
关于作者
王建新,转转架构部服务治理担任人,首要担任服务治理、RPC结构、分布式调用盯梢、监控系统等。爱技能、爱学习,欢迎联络沟通。
转转研制中心及业界小伙伴们的技能学习沟通渠道,定期共享一线的实战经验及业界前沿的技能论题。 关注公众号「转转技能」(综合性)、「大转转FE」(专注于FE)、「转转QA」(专注于QA),更多干货实践,欢迎沟通共享~