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

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

前言

在本系列之前的文章中,我们已经介绍了同步,异步,泛化调用等,也介绍了链路追踪的能力,本篇,我们将介绍一下 SOFARPC 中另一种内置的数据透传的能力。会依次介绍,数据透传的概念, SOFARPC 的设计原理,以及各种不同调用方式下的透传使用和详细说明,最后, 还会比较一下和 SOFATracer 的区别。欢迎大家与我们讨论交流。

数据透传介绍

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

而数据透传功能,就是指数据不需要以作为方法参数的形式在调用链路中进行传递,而是直接存储到调用上下文中,之后通过 RPC 的内置对象,进行传递,调用双端可从上下文中获取数据而不需要去关注数据的传输过程。

SOFARPC 提供的数据透传支持请求数据透传(客户端向服务端)和响应数据透传(服务端向客户端)。

SOFARPC 设计原理

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

image | left

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

不同调用方式的透传

我们知道,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 调用下的透传

使用示例

image | left

原理剖析

image | left

请求透传数据

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

响应透传数据

  1. 服务端设置响应透传数据到其调用上下文RpcInvokeContext.responseBaggage属性中,之后在ProviderBaggageFilter#invoke 方法中先从responseBaggage中取出响应透传数据并设置到SofaResponse.responseProps属性中;
  2. 客户端main线程被唤醒后,先从SofaResponse.responseProps中获取响应透传数据,之后将响应透传数据设置到其调用上下文RpcInvokeContext.responseBaggage中,最后业务代码就可以从调用上下文中获取响应透传数据了。

oneway 调用下的透传

使用示例

image | left

原理剖析

image | left

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

future 调用下的透传

使用示例

image | left

原理剖析

image | left

客户端获取响应透传数据

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

  1. SofaResponse.responseProps中获取响应透传数据
  2. 从 callback 对象中获取 main 线程的调用上下文
  3. 设置响应透传数据到 main 线程的调用上下文
  4. 将 main 线程上下文拷贝到当前的回调线程中

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

callback 调用下的透传

使用示例

image | left

原理剖析

image | left

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

与 SOFATracer 的比较

如果了解过 SOFATracer 的同学会有疑问,这个跟 Tracer 是不是有功能上的重叠呢?实际上。

SOFATracer 是蚂蚁开源的一个分布式链路追踪系统,SOFARPC 目前已经和 Tracer 做了集成,默认开启。和 Tracer 进行数据传递不同的是

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

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

参考

image | left | 216x216

长按关注,获取分布式架构干货

欢迎大家共同打造 SOFAStack https://github.com/alipay

相关文章
|
Kubernetes 大数据 工业大脑
入门必读!Apache Flink 零基础系列教程,30 天成长为 Flink 大神!
多位 Flink PMC 及核心贡献者出品,帮你建立系统框架体系,最详细的免费教程,Flink 入门必读经典!越早学习,越能抓住时代先机。
入门必读!Apache Flink 零基础系列教程,30 天成长为 Flink 大神!
|
数据采集 ice Sentinel
Google Earth Engine(GEE)——sentinel2数据介绍
Google Earth Engine(GEE)——sentinel2数据介绍
1331 0
Google Earth Engine(GEE)——sentinel2数据介绍
|
7月前
|
设计模式 SQL 安全
并发设计模式实战系列(13):双重检查锁定(Double-Checked Locking)
🌟 大家好,我是摘星!🌟今天为大家带来的是并发设计模式实战系列,第十三章,废话不多说直接开始~
380 0
|
7月前
|
监控 测试技术 开发者
IDEA项目调试你都会用了么,快看看是否有你不知道的调试技巧
在IntelliJ IDEA中,熟练运用调试工具可显著提升开发效率。通过设置断点、单步执行、变量监控等功能,快速定位问题并优化代码性能。此外,掌握多线程调试、异常处理及远程调试技巧也至关重要。为提高效率,建议合理使用条件断点、快捷键与日志监控,同时不断学习总结经验。若觉得有用,别忘了点赞收藏!
IDEA项目调试你都会用了么,快看看是否有你不知道的调试技巧
|
4月前
|
人工智能 JavaScript 前端开发
用 Go 语言轻松构建 MCP 服务器
本文介绍了使用 Go 语言构建 MCP 服务器的完整过程,涵盖创建服务器实例、注册工具、资源和提示词,以及通过 stdio 和 sse 模式启动服务的方法,帮助开发者快速集成 LLM 应用与外部系统。
|
12月前
|
存储 Java
Bitmap位图(Java实现)
本文介绍了使用Java实现一个简单的Bitmap,通过自定义byte数组存储数据,提供put和exist方法分别用于插入数据和查询数据是否存在。Bitmap利用位操作高效地管理大量布尔值,适用于空间优化的场景。代码中详细解释了位图的核心原理、方法实现及边界检查。后续计划探讨位图在海量数据去重中的应用及JDK BitSet源码分析。
764 7
|
前端开发 应用服务中间件 API
|
存储 关系型数据库 数据库
不直接使用文件存储?浅谈数据库的三级模式及重要概念
【5月更文挑战第21天】本文介绍数据库用于解决传统文件系统如Excel的数据冗余、不一致性和访问困难等问题。关系型数据库通过DBMS实现数据管理,包括外模式(用户视图)、概念模式(全局逻辑结构)和内模式(物理存储)。
441 1
不直接使用文件存储?浅谈数据库的三级模式及重要概念
|
消息中间件 容灾 物联网
【RocketMQ系列十四】RocketMQ中消息堆积如何处理
【RocketMQ系列十四】RocketMQ中消息堆积如何处理
2761 3
|
API Python
Python 的网络编程:如何使用 Python 发送和接收 HTTP 请求?
Python 的网络编程:如何使用 Python 发送和接收 HTTP 请求?
705 1