RPC框架泛调用原理及转转的实践

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的类结构生成序列化中间体,当然序列化中间体并非一定能在序列化框架中找到对应的类,有时候这个中间体是虚拟的。

普通RPC调用序列化原理

3.1 基于Java Bean的泛化调用

基于Java Bean的泛化调用是通过统一的Java Bean描述符(JavaBeanDescriptor)来描述POJO对象,它工作在序列化层之上,例如dubbo支持该种类型的泛化调用,在使用泛化调用时,直接传递JavaBeanDescriptor对象作为参数,基本原理如下图所示。

Java Bean泛化调用

该泛化调用的实现通用性比较强,与底层序列化无关,但是复杂度较高,需要RPC框架处理POJOJavaBeanDescriptor之间的转换。

3.2 基于序列化中间体的泛化调用

支持基于序列化中间体的泛化调用的RPC框架典型的如sofa-rpc,使用了sofa-hessian序列化框架,sofa-hessian是在hessian序列化框架基础上进行二次开发的,抽象出了序列化中间体,如GenericObjectGenericMapGenericArray等。

转转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框架需要实现JavaBeanDescriptorPOJO的转换功能,较为复杂。
  • 基于序列化中间体的泛化调用:优点是RPC框架实现简单,序列化框架原生支持或者仅需少量改造;缺点是与特定的序列化框架耦合。

在开发RPC框架时,具体选择哪种泛化调用实现方式,还需要结合实际情况做出选择。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
拍拍贷微服务rpc框架源码.zip # 拍拍贷微服务体系 拍拍贷微服务体系是拍拍贷基础框架部总结内部微服务多年实践,参考、吸收大量业内解决方案形成的适合中型互联网公司的微服务解决方案。 拍拍贷微服务体系主要组成部分: - Raptor rpc框架。 - Radar服务注册中心。 - Kong网关。 微服务实例启动之后,会自动注册到radar服务注册中心,实例启动正常后,kong网关周期性的将实例信息同步到kong的插件配置。微服务之间的调用、zuul网关调用微服务,都是通过域名进行调用,域名解析到kong网关。Kong网关根据域名和微服务的对应关系,对微服务实例进行负载均衡运算后,得到一个实例,最终进行调用。 拍拍贷微服务体系主要架构考虑: - 由Kong网关形成的集中式服务治理。降低由于客户端服务治理bugfix等引起的升级成本。 - 采用HTTP 1.1作为底层传输协议,从外部到内部无需进行协议转换。 - 采用HTTP 1.1 作为底层传输协议,不会引起原有基于HTTP协议的已有设施失效。 # Raptor微服务rpc组件 Raptor微服务rpc组件是拍拍贷基础框架部参考、借鉴了大量已有rpc框架rpc组件的设计,研发的一款基于google protobuf的轻量级,可扩展的rpc组件。 **Raptor设计理念:** - 微内核。Raptor核心实现raptor rpc必须的服务定义、protobuf序列化/反序列化、扩展接口和最小化实现。 - 可扩展。Raptor核心预留了Client、Endpoint等可扩展接口,提供相应的实现即可替换掉默认的实现。 - 模块化。Raptor核心不提供组装能力,raptor核心提供了rpc框架所需的核心组件,由外部框架进行组装。例如,raptor默认实现提供了基于spring-boot的组装方式。 **Raptor的价值:** - 契约驱动开发模式,以protobuf为契约,帮助企业规模化生产。 - JAVA语言开发,工程性好,保护原有技术投资。 - 预留兼容性,以protobuf为契约,符合社区技术趋势,为后续以protobuf为基础做技术升级留下兼容性。例如,引入grpc,业务契约无需改变。 - 灵活性,可采用集中治理,也可以采用客户端治理;可使用protobuf binary over HTTP也可以使用protobuf json over HTTP,服务提供方根据HTTP头自适应;为架构师留下灵活的选择余地。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值