【rpc通信框架】rpc服务治理框架高级特性
一、rpc服务治理框架高级特性
1、异步rpc
2、安全体系建设
3、分布式环境下快速定位问题
4、时钟轮应用
5、流量回放
6、动态分组,秒级扩容
7、没有接口的情况下进行rpc调用
8、兼容多种rpc协议
二、详解
1、异步rpc
目的:提升服务器的吞吐量(tps,qps),提升cpu利用率,调用异步化。
- 同步调用,不过是 RPC 框架在调用端的处理逻辑中主动执行了这个 Future 的 get 方法,让动态代理等待返回值;
- 异步调用则是 RPC 框架没有主动执行这个 Future 的 get 方法,用户可以从请求上下文中得到这个 Future,自己决定什么时候执行这个 Future 的 get 方法。
- 需要 RPC 框架提供一种回调方式,让业务逻辑可以异步处理,处理完之后调用 RPC 框架的回调接口,将最终的结果通过回调的方式响应给调用端。
CompletableFuture 的完全调用端和服务端完全异步化,是 Java8 原生支持的。试想一下,假如 RPC 框架能够支持
- 服务调用方发起 RPC 调用,直接拿到返回值 CompletableFuture 对象,之后就不需要任何额外的与 RPC 框架相关的操作了(如我刚才讲解 Future 方式时需要通过请求上下文获取 Future 的操作),直接就可以进行异步处理;
- 在服务端的业务逻辑中创建一个返回值 CompletableFuture 对象,之后服务端真正的业务逻辑完全可以在一个线程池中异步处理,业务逻辑完成之后再调用这个 CompletableFuture 对象的 complete 方法,完成异步通知;
- 调用端在收到服务端发送过来的响应之后,RPC 框架再自动地调用调用端拿到的那个返回值 CompletableFuture 对象的 complete 方法,这样一次异步调用就完成了。
2、安全体系建设
【目的】
RPC 来说,我们所关心的安全问题不会有公网应用那么复杂,
- 保证让服务调用方能拿到真实的服务提供方 IP 地址集合
- 服务提供方可以管控调用自己的应用。
【解决方案】
服务提供方,控制调用方
- 调用方能不能调用相关接口,是由服务提供方说了算。服务提供方对允许调用的客户端标识进行配置化。
- 加密算法里面有一种叫做不可逆加密算法吗?HMAC 就是其中一种具体实现。服务提供方应用里面放一个用于 HMAC 签名的私钥,在授权平台上用这个私钥为申请调用的调用方应用进行签名,这个签名生成的串就变成了调用方唯一的身份。服务提供方在收到调用方的授权请求之后,我们只要需要验证下这个签名跟调用方应用信息是否对应得上就行了,这样集中式授权的瓶颈也就不存在了。
服务提供方,控制服务注册
- 服务提供方启动的时候,需要把接口实例在注册中心进行注册登记。
- 可以利用这个流程,注册中心可以在收到服务提供方注册请求的时候,验证下请求过来的应用是否跟接口绑定的应用一样,只有相同才允许注册,否则就返回错误信息给启动的应用,从而避免假冒的服务提供者对外提供错误服务。
3、分布式环境下快速定位问题
【目的】
RPC 在分布式环境下如何快速定位问题
【解决方案】
方法 1:借助合理封装的异常信息。将异常的服务器ip和接口信息,异常原因封装清楚向上抛。
方法2:借助分布式链路追踪系统。
- 分布式链路跟踪有 Trace 与 Span 的概念,什么意思呢,我逐一解释。
- Trace 就是代表整个链路,每次分布式都会产生一个 Trace,每个 Trace 都有它的唯一标识即 TraceId,在分布式链路跟踪系统中,就是通过 TraceId 来区分每个 Trace 的。
- Span 就是代表了整个链路中的一段链路,也就是说 Trace 是由多个 Span 组成的。在一个 Trace 下,每个 Span 也都有它的唯一标识 SpanId,而 Span 是存在父子关系的
4、时钟轮应用
【目的】
确保调用端高效的处理调用超时问题。异步的超时调度线程,每次只轮询在一个槽位中超时请求,减少cpu的占用,提升服务的性能。
【解决方案】
https://www.cnblogs.com/luozhiyun/p/12075326.html
5、流量回放
【目的】
提升测试准确性,将线上的请求信息和响应信息进行记录,然后将信息拿出来重新发起业务调用,验证服务代码升级后不会有bug
【解决方案】
6、动态分组,秒级扩容
【目的】
服务分成多组A和B,每个组对外提供一定的容量能力。当A组的容量达到瓶颈时,需要实现快速扩容,提升A的容量。
【解决方案】
当A的容量达到瓶颈时,借用B组的机器进行业务处理。
7、没有接口的情况下进行rpc调用
【目的】
统一测试平台,调用多个rpc服务(无需集成rpc服务的客户端jar包)
【解决方案】
泛化调用,利用rpc框架的插件能力,提供特定的泛化调用序列化算法。
【客户端】请求信息-->Map-->序列化-->【网络传输】-->反序列化为Map--->反序列化为服务端入参对象-->反射执行业务逻辑
在服务提供方提供的接口 API 中,被调用的方法的入参类型是一个对象,那么使用泛化调用功能的调用端,可以使用 Map 类型的对象,之后通过泛化调用专属的序列化方式对这个 Map 对象进行序列化
服务端收到消息后,再通过泛化调用专属的序列化方式将其反序列成对象。
8、兼容多种rpc协议
【目的】
- 当服务提供方式要更换rpc协议时,需要兼容客户端的不同的rpc协议调用。
- 既然应用之间的通信都是通过 RPC 来完成的,而能够完成 RPC 通信的工具有很多,比如像 Web Service、Hessian、gRPC 等都可以用来充当 RPC 使用。这些不同的 RPC 框架都是随着互联网技术的发展而慢慢涌现出来的,而这些 RPC 框架可能在不同时期会被我们引入到不同的项目中解决当时应用之间的通信问题,这样就导致我们线上的生成环境中存在各种各样的 RPC 框架。
【解决方案】
A协议-->Map对象-->B协议
- 要让新的 RPC 同时支持多种 RPC 调用,关键就在于要让新的 RPC 能够原地支持多种协议的请求。怎么才能做到?在[第 02 讲] 我们说过,协议的作用就是用于分割二进制数据流。每种协议约定的数据包格式是不一样的,而且每种协议开头都有一个协议编码,我们一般叫做 magic number。
- 当 RPC 收到了数据包后,我们可以先解析出 magic number 来。获取到 magic number 后,我们就很容易地找到对应协议的数据格式,然后用对应协议的数据格式去解析收到的二进制数据包。
- 协议解析过程就是把一连串的二进制数据变成一个 RPC 内部对象,但这个对象一般是跟协议相关的,所以为了能让 RPC 内部处理起来更加方便,我们一般都会把这个协议相关的对象转成一个跟协议无关的 RPC 对象。
- 这是因为在 RPC 流程中,当服务提供方收到反序列化后的请求的时候,我们需要根据当前请求的参数找到对应接口的实现类去完成真正的方法调用。如果这个请求参数是跟协议相关的话,那后续 RPC 的整个处理逻辑就会变得很复杂。
- 当完成了真正的方法调用以后,RPC 返回的也是一个跟协议无关的通用对象,所以在真正往调用方写回数据的时候,我们同样需要完成一个对象转换的逻辑,只不过这时候是把通用对象转成协议相关的对象。
- 在收发数据包的时候,我们通过两次转换实现 RPC 内部的处理逻辑跟协议无关,同时保证调用方收到的数据格式跟调用请求过来的数据格式是一样的。整个流程如下图所示: