【剖析 | SOFARPC 框架】系列之 SOFARPC 数据透传剖析

SOFA

Scalable Open Financial Architecture

是蚂蚁金服自主研发的金融级分布式中间件,包含了构建金融级云原生架构所需的各个组件,是在金融场景里锤炼出来的最佳实践。

本文为《剖析 | SOFARPC 框架》第八篇,作者水寒,目前就职于网易。

《剖析 | SOFARPC 框架》系列由 SOFA 团队和源码爱好者们出品,
项目代号:,官方目录目前已经全部认领完毕,文末提供了已完成的文章目录。


【剖析 | SOFARPC 框架】系列之 SOFARPC 数据透传剖析

前言

在《剖析 | SOFARPC 框架》系列之前的文章中,我们已经介绍了同步,异步,泛化调用等,也介绍了链路追踪的能力。

本篇,我们将介绍一下 SOFARPC 中另一种内置的数据透传的能力。依次介绍,数据透传的概念, SOFARPC 的设计原理,以及各种不同调用方式下的透传使用和详细说明,最后, 还会比较一下和 SOFATracer 的区别。欢迎大家与我们讨论交流。

数据透传介绍

首先,我们知道,在 RPC 调用中,数据的传递,是通过接口方法参数来传递的,需要接口方定义好一些参数允许传递才可以。在一些场景下,我们希望,能够更通用的传递一些参数,比如一些标识性的信息。业务方可能希望,在每一次调用请求中都能够传递一些自定义的信息到下游。甚至也希望下游能够将一些数据传递回来。

而数据透传功能,就是指数据不需要以作为方法参数的形式在调用链路中进行传递,而是直接存储到调用上下文中,之后通过 RPC 的内置对象,进行传递,调用双端可从上下文中获取数据而不需要去关注数据的传输过程。SOFARPC 提供的数据透传支持请求数据透传(客户端向服务端)和响应数据透传(服务端向客户端)

SOFARPC 设计原理

这里主要是介绍一下,实现的核心原理,更加具体的每种调用方式的透传,在后文中都会详细介绍。

【剖析 | SOFARPC 框架】系列之 SOFARPC 数据透传剖析

  • 用户通过 SOFARPC 提供的 API 进行数据传递设置
  • SOFARPC 在调用传输前,将透传的数据进行打包获取
  • 进行正常的序列化和反序列化
  • SOFARPC 在反序列化时将用户设置的透传数据写回 Context
  • 服务端用户即可进行获取使用

不同调用方式的透传

我们知道,SOFARPC 目前支持四种调用模式,如果没有阅读过之前文章的同学,可以阅读一下 SOFARPC 同步异步实现剖析 。四种调用模式下,请求透传数据的原理都是一样的,服务端设置响应透传数据的原理也是一样的,只是客户端获取响应透传数据的方式有所不同(后三种模式只介绍客户端获取响应透传数据的原理)。

因此我们会介绍下不同调用方式的透传细节,并介绍其使用方式,方便大家理解。以下为了方便说明,我们会使用如下的接口示例

接口服务

public interface HelloService {
    String sayHello(String string);
}

服务实现

public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String string) {
        // 获取请求透传数据并打印
        System.out.println("service receive reqBag -> " + RpcInvokeContext.getContext().getRequestBaggage("req_bag"));
        // 设置响应透传数据到当前线程的上下文中
        RpcInvokeContext.getContext().putResponseBaggage("resp_bag", "s2c");
        return "hello " + string + " !";
    }
}

后续的所有调用模式都使用HelloServiceImpl这个服务实现。(示例代码在 SOFARPC 的测试 case 中都要对应的示例,大家可以对应阅读)

对用户可见的操作 API 只有一个就是 RpcInvokeContext,在 SOFABoot 和 SOFARPC 下都适用,当然如果你了解 SOFARPC 的 Filter 机制,也可以通过扩展这个来实现。

一、sync 调用下的透传
使用示例
【剖析 | SOFARPC 框架】系列之 SOFARPC 数据透传剖析

原理剖析
【剖析 | SOFARPC 框架】系列之 SOFARPC 数据透传剖析

请求透传数据
客户端首先在 main 线程(图中的user thread)中设置请求透传数据到其调用上下文RpcInvokeContext.requestBaggage属性中,之后在调用过程中从requestBaggage中取出请求透传数据并设置到SofaRequest.requestProps属性中。

服务端接收到请求SofaRequest对象后,在其调用链中的 ProviderBaggageFilter#invoke 方法中会先从SofaRequest.requestProps中取出请求透传数据并设置到当前服务端线程的调用上下文RpcInvokeContext.requestBaggage属性中,最后业务代码就可以从调用上下文中获取请求透传数据了。

响应透传数据
服务端设置响应透传数据到其调用上下文RpcInvokeContext.responseBaggage属性中,之后在ProviderBaggageFilter#invoke 方法中先从responseBaggage中取出响应透传数据并设置到SofaResponse.responseProps属性中。

客户端main线程被唤醒后,先从SofaResponse.responseProps中获取响应透传数据,之后将响应透传数据设置到其调用上下文RpcInvokeContext.responseBaggage中,最后业务代码就可以从调用上下文中获取响应透传数据了。

二、oneway 调用下的透传
使用示例

【剖析 | SOFARPC 框架】系列之 SOFARPC 数据透传剖析

原理剖析

【剖析 | SOFARPC 框架】系列之 SOFARPC 数据透传剖析

在 oneway 模式下,客户端不接受服务端响应,也不会获取响应透传数据。

三、future 调用下的透传
使用示例
【剖析 | SOFARPC 框架】系列之 SOFARPC 数据透传剖析

原理剖析

【剖析 | SOFARPC 框架】系列之 SOFARPC 数据透传剖析

客户端获取响应透传数据
future 模式在 SOFARPC 内部会被转化为 callback 的方式进行调用,在 callback 对象中会存储main线程的调用上下文;当客户端接收到响应时,会执行该 callback 对象的回调函数,在其回调函数中,对于响应透传数据,会做如下操作:

从SofaResponse.responseProps中获取响应透传数据

从 callback 对象中获取 main 线程的调用上下文

设置响应透传数据到 main 线程的调用上下文

将 main 线程上下文拷贝到当前的回调线程中

实际上,第三步与第四步在 SOFARPC 源码中顺序相反,本文这样解读是为了更容易理解。这样无论是 future 模式(从 main 线程的调用上下文获取响应透传数据)还是 callback 模式(从回调线程的调用上下文获取响应透传数据),都可以顺利的获取到响应透传数据。

四、callback 调用下的透传
使用示例

【剖析 | SOFARPC 框架】系列之 SOFARPC 数据透传剖析

原理剖析

【剖析 | SOFARPC 框架】系列之 SOFARPC 数据透传剖析

与future模式原理一样,只是最终业务代码中是从回调线程而不是main线程的调用上下文中获取响应透传数据。

与 SOFATracer 的比较

如果了解过 SOFATracer 的同学会有疑问,这个跟 Tracer 是不是有功能上的重叠呢?实际上 SOFATracer 是蚂蚁开源的一个分布式链路追踪系统,SOFARPC 目前已经和 Tracer 做了集成,默认开启。

和 Tracer 进行数据传递不同的是

  • SOFARPC 的数据透传更偏向业务使用,而且可以在全链路中进行双向传递,调用方可以传给服务方(请求透传数据),服务方也可以传递信息给调用方(响应透传数据),SOFATracer 更加偏向于中间件和业务无感知的数据的传递,并且只能进行单向传递。也就是向下传递,调用方并不能获取服务提供方的透传数据。
  • SOFARPC 的透传可以选择性地不在全链路中透传(主动清除调用上下文数据),而 Tracer 中如果传递大量信息,会在整个链路中传递。可能对下游业务会有影响。

所以整体来看,两种方式各有利弊,在有一些和业务相关的透传数据的情况下,可以选择 SOFARPC 的透传。

上一篇:【剖析 | SOFARPC 框架】系列之 SOFARPC 泛化调用实现剖析


下一篇:蚂蚁金服通信框架SOFABolt解析 | 编解码机制