23丨如何在没有接口的情况下进行 RPC 调用?

简介: 本文介绍RPC泛化调用的应用场景及实现原理,重点解决无接口API时如何发起调用的问题,适用于统一测试平台与服务网关等场景,通过GenericService与专属序列化插件实现灵活调用。

应用场景有哪些?

在 RPC 运营的过程中,让调用端在没有接口 API 的情况下发起 RPC 调用的需求,不只是一个业务方和我提过,这里我列举两个非常典型的场景例子。

场景一:我们要搭建一个统一的测试平台,可以让各个业务方在测试平台中通过输入接口、分组名、方法名以及参数值,在线测试自己发布的 RPC 服务。这时我们就有一个问题要解决,我们搭建统一的测试平台实际上是作为各个 RPC 服务的调用端,而在 RPC 框架的使用中,调用端是需要依赖服务提供方提供的接口 API 的,而统一测试平台不可能依赖所有服务提供方的接口 API。我们不能因为每有一个新的服务发布,就去修改平台的代码以及重新上线。这时我们就需要让调用端在没有服务提供方提供接口的情况下,仍然可以正常地发起 RPC 调用。

场景二:我们要搭建一个轻量级的服务网关,可以让各个业务方用 HTTP 的方式,通过服务网关调用其它服务。这时就有与场景一相同的问题,服务网关要作为所有 RPC 服务的调用端,是不能依赖所有服务提供方的接口 API 的,也需要调用端在没有服务提供方提供接口的情况下,仍然可以正常地发起 RPC 调用。

这两个场景都是我们经常会碰到的,而让调用端在没有服务提供方提供接口 API 的情况下仍然可以发起 RPC 调用的功能,在 RPC 框架中也是非常有价值的。

怎么做?

RPC 框架要实现这个功能,我们可以使用泛化调用。那什么是泛化调用呢?我们带着这个问题,先学习下如何在没有接口的情况下进行 RPC 调用。

我们先回想下我在基础篇讲过的内容,通过前面的学习我们了解到,在 RPC 调用的过程中,调用端向服务端发起请求,首先要通过动态代理,正如 第 05 讲 中我说过的,动态代理可以帮助我们屏蔽 RPC 处理流程,真正地让我们发起远程调用就像调用本地一样。

那么在 RPC 调用的过程中,既然调用端是通过动态代理向服务端发起远程调用的,那么在调用端的程序中就一定要依赖服务提供方提供的接口 API,因为调用端是通过这个接口 API 自动生成动态代理的。那如果没有接口 API 呢?我们该如何让调用端仍然能够发起 RPC 调用呢?

所谓的 RPC 调用,本质上就是调用端向服务端发送一条请求消息,服务端接收并处理,之后向调用端发送一条响应消息,调用端处理完响应消息之后,一次 RPC 调用就完成了。那是不是说我们只要能够让调用端在没有服务提供方提供接口的情况下,仍然能够向服务端发送正确的请求消息,就能够解决这个问题了呢?

没错,只要调用端将服务端需要知道的信息,如接口名、业务分组名、方法名以及参数信息等封装成请求消息发送给服务端,服务端就能够解析并处理这条请求消息,这样问题就解决了。过程如下图所示:

现在我们已经清楚了解决问题的关键,但 RPC 的调用端向服务端发送消息是需要以动态代理作为入口的,我们现在得继续想办法让调用端发送我刚才讲过的那条请求消息。

我们可以定义一个统一的接口(GenericService),调用端在创建 GenericService 代理时指定真正需要调用的接口的接口名以及分组名,而 GenericService 接口的 $invoke 方法的入参就是方法名以及参数信息。

这样我们传递给服务端所需要的所有信息,包括接口名、业务分组名、方法名以及参数信息等都可以通过调用 GenericService 代理的 $invoke 方法来传递。具体的接口定义如下:

class GenericService {
  Object $invoke(String methodName, String[] paramTypes, Object[] params);
  
}

这个通过统一的 GenericService 接口类生成的动态代理,来实现在没有接口的情况下进行 RPC 调用的功能,我们就称之为泛化调用。

通过泛化调用功能,我们可以解决在没有服务提供方提供接口 API 的情况下进行 RPC 调用,那么这个功能是否就完美了呢?

回顾下 第 17 讲 我过的内容,RPC 框架可以通过异步的方式提升吞吐量,还有如何实现全异步的 RPC 框架,其关键点就是 RPC 框架对 CompletableFuture 的支持,那么我们的泛化调用是否也可以支持异步呢?

当然可以。我们可以给 GenericService 接口再添加一个异步方法 $asyncInvoke,方法的返回值就是 CompletableFuture,GenericService 接口的具体定义如下:

class GenericService {
  Object $invoke(String methodName, String[] paramTypes, Object[] params);
  CompletableFuture<Object> $asyncInvoke(String methodName, String[] paramTypes, Object[] params);
}

学到这里相信你已经对泛化调用的功能有一定的了解了,那你有没有想过这样一个问题?在没有服务提供方提供接口 API 的情况下,我们可以用泛化调用的方式实现 RPC 调用,但是如果没有服务提供方提供接口 API,我们就没法得到入参以及返回值的 Class 类,也就不能对入参对象进行正常的序列化。这时我们会面临两个问题:

问题 1:调用端不能对入参对象进行正常的序列化,那调用端、服务端在接收到请求消息后,入参对象又该如何序列化与反序列化呢?

回想下 第 07 讲,在这一讲中我讲解了如何设计可扩展的 RPC 框架,我们通过插件体系来提高 RPC 框架的可扩展性,在 RPC 框架的整体架构中就包括了序列化插件,我们可以为泛化调用提供专属的序列化插件,通过这个插件,解决泛化调用中的序列化与反序列化问题。

问题 2:调用端的入参对象(params)与返回值应该是什么类型呢?

在服务提供方提供的接口 API 中,被调用的方法的入参类型是一个对象,那么使用泛化调用功能的调用端,可以使用 Map 类型的对象,之后通过泛化调用专属的序列化方式对这个 Map 对象进行序列化,服务端收到消息后,再通过泛化调用专属的序列化方式将其反序列成对象。

总结

今天我们主要讲解了如何在没有接口的情况下进行 RPC 调用,泛化调用的功能可以实现这一目的。

这个功能的实现原理,就是 RPC 框架提供统一的泛化调用接口(GenericService),调用端在创建 GenericService 代理时指定真正需要调用的接口的接口名以及分组名,通过调用 GenericService 代理的 $invoke 方法将服务端所需要的所有信息,包括接口名、业务分组名、方法名以及参数信息等封装成请求消息,发送给服务端,实现在没有接口的情况下进行 RPC 调用的功能。

而通过泛化调用的方式发起调用,由于调用端没有服务端提供方提供的接口 API,不能正常地进行序列化与反序列化,我们可以为泛化调用提供专属的序列化插件,来解决实际问题。

课后思考

在讲解泛化调用时,我讲到服务端在收到调用端通过泛化调用的方式发送过来的请求时,会使用泛化调用专属的序列化插件实现对其进行反序列化,那么服务端是如何判定这个请求消息是通过泛化调用的方式发送过来的消息呢?

笔者认为:可以在协议中设置序列化类型,在服务端接受到后,就知道用什么插件反序列化了

相关文章
|
存储 资源调度 JavaScript
JavaScript日期时间操作完整指南!(下)
JavaScript日期时间操作完整指南!(下)
679 0
|
4月前
|
SQL 算法 Java
MybatisPlus讲义
本项目基于若依框架与AI技术,打造智慧养老管理系统。涵盖来访、入住、服务、财务等模块,分管理后台与家属端,结合Vue3、SpringBoot、Redis、物联网等技术,融合大模型与AI工具,助力高效开发,响应中国智慧养老发展趋势。
|
安全 应用服务中间件 网络安全
https安全证书如何申请 https证书申请流程及费用
  随着谷歌、百度等主流浏览器大力支持鼓励网站安装SSL证书进行https加密,保障网站安全,网站安装https证书已经成为一种趋势。那么,https安全证书如何申请?申请https证书需要什么?https证书申请多少钱?      一、什么是https证书?     https证书也就是SSL证书,网站通过申请SSL证书将http协议升级为https加密协议,搭建加密传输、身份认证的网络安全通道。
6576 0
|
Java 应用服务中间件
WAS生成及分析javacore和heapdump
WAS生成及分析javacore和heapdump
|
NoSQL Redis 数据安全/隐私保护
redis设置密码
redis设置密码
1967 1
|
API
掌握 HTTP 请求的艺术:理解 cURL GET 语法
掌握 cURL GET 请求的语法和使用方法是 Web 开发和测试中的基本技能。通过灵活运用 cURL 提供的各种选项,可以高效地与 API 进行交互、调试网络请求,并自动化日常任务。希望本文能帮助读者更好地理解和使用 cURL,提高工作效率和代码质量。
1563 7
|
Java 数据库连接 Spring
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could
这个错误通常出现在使用Spring Boot进行数据库连接时。错误信息表明Spring Boot未能配置一个DataSource,因为没有指定'url'属性,并且没有发现默认的数据库连接。
6214 0
|
运维 Kubernetes 监控
PaaS平台
【7月更文挑战第2天】PaaS平台
651 58
|
安全 NoSQL 关系型数据库
阿里云数据库:构建高性能与安全的数据管理系统
在企业数字化转型过程中,数据库是支撑企业业务运转的核心。随着数据量的急剧增长和数据处理需求的不断增加,企业需要一个既能提供高性能又能保障数据安全的数据库解决方案。阿里云数据库产品为企业提供了一站式的数据管理服务,涵盖关系型、非关系型、内存数据库等多种类型,帮助企业构建高效的数据基础设施。
789 2

热门文章

最新文章

下一篇
开通oss服务