写点什么

流量治理的基石——基于字节码增强的全链路流量标签透传

作者:华为云开源
  • 2023-09-26
    广东
  • 本文字数:6241 字

    阅读完需:约 20 分钟

流量治理的基石——基于字节码增强的全链路流量标签透传

 作者:李来      华为云高级软件工程师

一、全链路流量标签透传

在微服务架构中,流量标签用于对流量进行标记和分类,能够在微服务之间实现更精细的路由、负载均衡和流控等流量治理能力。以 HTTP 报文为例,每一条 header 都可以是一条流量标签,比如 x-sermant-version: v1 表示通过 Sermant 流量染色添加的标签,可以用于标识该请求流量对应的的版本信息。


Accept: */*

Accept-Encoding: gzip, deflate, br

Accept-Language: zh-CN,zh;q=0.9,en;q=0.8

Connection: keep-alive

Content-Length: 1000

Content-Type: application/json

Host: api.github.com

Origin: https://github.com

Referer: https://github.com

x-sermant-version: v1   //自定义 header,表示通过 Sermant 流量染色添加的标签


流量标签可以在微服务治理场景中发挥作用的核心是流量标签能够在调用链上的微服务之间传递,也即——全链路流量标签透传。全链路流量标签透传是指在分布式系统中,将请求的标签信息(例如上面报文中的 x-sermant-version: v1)从请求的起点一直透传到请求的终点,以便于在整个请求链路中进行流量治理。流量标签透传可以在各种服务治理场景中发挥关键作用。例如,全链路灰度发布场景下,微服务调用方可以根据请求中携带的流量标签信息将请求路由到灰度的新版本微服务实例。在流量控制场景中,流量标签透传还可以用于实施精准的流量控制策略,例如限流、熔断和降级,防止系统在高负载下崩溃。

二、如何实现全链路标签透传

在 Java 中目前主流的全链路标签透传的实现方式分为两种,一种是通过在 SDK 中集成标签传递能力,配合 API 网关来实现,另一种则是通过应用上挂载的 JavaAgent 来实现。

下图是 Spring Cloud Tencent 在 SDK 中实现的流量标签透传,该图展示了一个通过 API 网关来染色的金丝雀发布场景。金丝雀发布(canary)是指在生产环境上引一部分实际流量对一个新版本进行测试,测试新版本的性能和表现,在保证系统整体稳定运行的前提下,尽早发现新版本在实际环境上的问题。这种 SDK 实现的标签透传方式一般使用网关来添加染色标签,然后在使用 Spring Cloud Tencent 微服务框架的应用中来透传标签,例如图中的用户中心、积分中心、活动中心会透传网关染色的 canary=true 标签。



图 - Spring Cloud Tencent 流量标签透传在金丝雀发布场景的应用

注:图片来源图片来源https://github.com/polarismesh/polaris/issues/631


通过 JavaAgent 来实现流量标签的透传,典型的例子是 Skywalking 对于链路 trace 信息的透传。在应用程序中嵌入 Skywalking 的 JavaAgent,通过在代码中插入埋点,它会在调用链的入口节点处生成一个标记链路的 traceId,然后在链路中的各个节点对调用链信息的进行传递,收集应用程序的调用链路信息。Skywalking 的关注的重心是链路的追踪,也就是调用链信息的标签透传,透传的标签的来源是在入口处生成一个唯一的 traceId,至于通用的流量染色能力,并不是 Skywaling 的场景。

不同的流量标签透传实现方式各异,也有各自适用的场景。spring-cloud-tencent 需要通过引入 SDK 来使用,Skywalking 的场景主要在于链路追踪,并不能很好的支持自定的标签染色和透传。以上都有各自的局限性,适用场景范围较小。

在微服务治理领域,一个自定义场景丰富,适用面广泛的全链路标签透传解决方案是流量治理多样化发展的基石。作为服务网格,Sermant 提供了一套支持自定义的流量染色和标签透传的全链路标签透传方案。

三、Sermant 在全链路流量标签透传的探索

Sermant 是基于 Java 字节码增强技术的无代理服务网格,不仅是一个开箱即用的服务治理工具,也同样是一个易用的服务治理能力开发框架。用户只需要在 Java 应用启动时添加一行 -javaagent:/xxx/sermant-agent.jar 即可一键以非侵入的方式接入 Sermant 的服务治理功能。

上文提到,全链路标签透传包含染色和透传两个部分。Sermant 针对这两部分,提供两个插件来分别独立实现流量染色和标签透传的能力,分别是标签路由插件(目前染色能力放在路由插件中,最终将拆分成独立的流量染色插件,结合标签透传,为多种多样的流量治理场景提供基础能力)以及流量标签透传插件。

下图为 Sermant 的整体使用方式,同样用第二章中提到的金丝雀发布场景来进行说明。各服务实例通过-javaagent 命令挂载 Sermant 启动,通过动态配置中心可以在服务运行期间动态地修改流量染色的规则以及标签透传的规则,并推送至各服务实例的 Sermant。在入口处接收到外部请求流量时,入口服务实例的 Sermant 会去匹配当前流量是否符合规则,如符合则进行染色,比如图中稳定版本的服务 A。各个服务实例的 Sermant 会匹配流量标签透传的规则,来识别哪些标签需要往下游透传,图中以 V1 版本标签需要透传来演示,整个流程实现了微服务的金丝雀发布能力。


图 – Sermant 全链路流量标签透传在金丝雀发布场景的示例


在 Sermant 中流量染色能力和流量标签透传能力作为基础,可以为其他插件更复杂的流量治理场景提供底座能力,使得流量治理能力的开发变得更加简单。因流量染色和标签透传为 Sermant 的独立插件,基于 API 网关进行流量染色的用户可以选择不启用 Sermant 的流量染色能力。Sermant 中基于全链路流量标签透传的高阶服务治理能力的依赖关系如下所示:



图 – Sermant 服务治理能力依赖关系


3.1 流量染色

流量染色的本质就是使用不同的标签来标识不同的流量类型。当前版本中,Sermant 先针对 Dubbo 和 SpringCloud 两种主流微服务框架适配了流量染色能力。流量染色目前位于 Sermant 的标签路由插件中,可作为独立能力使用。流量染色的主要处理流程如下:



图 – Sermant 的流量染色流程


首先在动态配置中心,我们可以下发服务粒度的染色规则,用于判断入方向的流量是否符合特定条件,若符合则对该流量进行染色,在同一调用链的出方向流量中携带该染色标签。在 Sermant 中,染色规则的数据模型如下:

- kind: route.sermant.io/lane # 路由规则为染色规则的类型    description: lane  # 规则描述    rules:      - precedence: 1        match:          method: get # http请求方式,不配置表示匹配          path: "/foo" # http请求路径,不配置表示匹配          protocol: http # 流量入口为http协议,http协议只会匹配headers/parameters          headers: # http headers匹配,不配置表示匹配            id:              exact: '1'          parameters: # http url parameters匹配,不配置表示匹配            name:              exact: 'foo'        route:          - tag-inject:              x-sermant-flag1: gray1            weight: 60
复制代码


上述规则的含义为,我们尝试匹配 precedence: 1 这条规则,即流量入口为 http 协议的请求,如果请求接口 url 为/foo,请求方式为 get,headers 中 id 等于 1,url parameters 中 name 等于 foo,就对满足条件的 60% 流量打上【x-sermant-flag1: gray1】的标记,并在调用下游时进行传递。在染色规则中,我们也支持下发多条匹配规则并配置优先级。流量染色能力的使用可参考官网使用文档

在实际应用中,可以根据需要自定义配置规则,例如我们需要对 http 请求的/getPrice 这个接口的流量染色,如果要给携带 city=Shanghai 或者 city=Hangzhou 的全部流量在后续链路中都加上【x-sermant-region: east】染色标记,那么规则就可以如下配置。

- kind: route.sermant.io/lane    description: lane    rules:      - precedence: 1        match:          method: get           path: "/getPrice"          protocol: http          headers:             city:              in: ['Shanghai', 'Hangzhou']      route:          - tag-inject:              x-sermant-region: east            weight: 100
复制代码


总之,染色的规则可以自定义配置,并且支持运行时动态修改并可立即生效,若无需染色,则删除动态配置即可。

3.2 流量标签透传

在经过 3.1 中的步骤对流量进行染色后,就可以利用 Sermant 的流量标签透传能力将染色标签进行全链路透传。Sermant 对于流量标签透传分为两大类:跨进程透传和进程内透传。跨进程透传是指在不同的服务实例进程中传递流量标记,例如 http 请求的客户端和服务端。进程内透传是指在一个服务实例的进程内传递流量标签,包括线程内传递和跨线程传递。

如下图所示,根据需要适配的标签传递的类型,我们又分为接口调用(包括 http 协议和 rpc 协议)和消息队列(包括 kafka、RocketMQ 等等)两种类型。


图 – Sermant 中 http/rpc 请求的标签透传过程



图 – Sermant 中消息队列的标签透传过程


3.2.1 跨进程透传

跨进程流量标签透传包括 http 协议、rpc 协议以及消息队列。我们借助 http 请求的 header、Dubbo 请求的 attachment、grpc 请求的 metadata、kafka 消息的 header 等来将流量标签在全链路中透传下去。

在 Sermant 流量标签透传插件中,对于客户端,我们利用字节码增强在 http、rpc 发送请求以及消息队列生产消息处构造切面,并在切面处获取当前进程内的流量标签,在发送请求时或生产消息时将流量标签注入 header、attachment 等。对于服务端,在 http、rpc 接收请求以及消息队列消费消息的切面处,将 header、attachment 等里面的流量标签取出,在服务端进程内继续透传。一次 http 或 rpc 的跨进程标签传递过程如下图所示:



图 – Sermant 跨进程透传标签的过程(http/rpc)

消息队列的标签透传与之类似,不同之处在于流量标记会在生产时发送到中间件的服务端,消费时从服务端拉取消息并解析出流量标签。

3.2.2 进程内透传

流量标签在进程内透传的情况分为两种:线程内和跨线程。线程内的场景 Sermant 按照常规做法,使用 ThreadLocal 线程变量来传递标签,此处不作赘述。

跨线程的场景,主要分为三类:直接 new Thread()、普通线程池和定时线程池。针对 new Thread(),使用 JDK 原生的InheritableThreadLocal来在父子线程中传递标记。

针对普通线程池,Sermant 拦截了它的 execute 和 submit 方法,在执行新的线程任务时,将父线程的流量标记透传至 Runnable 或 Callable 对象中,在子线程中就可以继续将流量标签传递下去。定时线程池与之类似,通过拦截 schedule、scheduleAtFixedRate 以及 scheduleWithFixedDelay 方法,将父线程流量标签传递至子线程。



图 – Sermant 跨线程透传标签的过程

以上三种方式可基本覆盖跨线程的所有场景,基于对它们的增强,Sermant 得以实现跨线程的流量标签透传。

3.2.3 流量标签透传的动态配置

流量标签透传也支持动态配置是否开启以及透传哪些标签,如下所示:

tag.transmission.config:  # 是否开启流量标签透传  enabled: true  # 需要透传的流量标签的key的匹配规则, 支持等于、前缀、后缀匹配  matchRule:    # 精确匹配    exact: ["id", "name"]    # 前缀匹配    prefix: ["x-sermant"]    # 后缀匹配    suffix: [""]
复制代码


当前支持对需要透传标签的键精确匹配、前缀匹配以及后缀匹配方式。在上一章介绍的染色规则配置完成后,再把染色标签配置在流量标签透传的配置中,这样就可以实现全链路标签透传从入口染色到整条链路传递的完整解决方案。

四、Sermant 全链路流量标签透传的应用场景

全链路流量标签透传是微服务流量治理的核心基础,它将流量标签这一关键钥匙准确地传递给每一个服务实例,让流量可以按照规划的轨迹流动。全链路流量标签透传可以应用在诸多服务治理场景,如全链路灰度发布、流控等。

4.1 基于全链路流量标签透传的全链路灰度发布场景

在全链路灰度发布场景中,通常需要将新版本的代码逐步地发布到生产环境中,以便在生产环境中进行测试和验证。如下图所示,gray1 版本只部署了部分服务,gray2 版本部署了全部服务。利用全链路流量标签透传的能力,可以根据服务实例的灰度版本信息,来让流量沿着灰度链路进行流转。稳定版本的流量只调用稳定版本的服务实例,灰度版本的流量优先调用当前灰度版本实例,如果中间某一服务不存在灰度实例,则调用稳定版本的实例。无论中间某一次调用的服务是稳定版本还是灰度版本,流量标签持续透传,灰度流量永远优先选择灰度版本进行路由。



图 – 利用 Sermant 来实现全链路灰度发布的流程


在当前版本的 Sermant 中,标签路由插件包含了流量染色以及标签路由的能力(功能独立,最终将拆为两个独立插件),流量标签透传插件支持了标签透传能力。上图全链路灰度发布场景的实现只需要同时挂载这两个插件即可实现。使用的主要流程为:

(1)标签路由插件配置流量的染色规则,比如灰度流量添加 x-sermant-version:gray1,x-sermant-version:gray2 标签。(该步骤可选,可用 API 网关等其他方式染色)

(2) 标签透传插件配置 x-sermant-version 键的标签在全链路中进行透传。

(3)标签路由插件配置流量的全链路路由规则,使得 gray1 流量优先调用 gray1 版本实例,使得 gray2 流量优先调用 gray2 版本实例。

4.2 基于全链路流量标签透传的流控场景

在流量控制场景中,也可以利用标签透传的特性来通过流量标签进行精细化的流量控制。通常我们可以通过流量的 url、header、token、请求参数等来对流量做分组。比如在双 11 这类场景中,服务的单个 API 提供的能力是有限的,但是实际的峰值调用需求可能是极限 QPS 的 2 倍,这种场景下可以利用 url 来对流量进行分组,对不同类型的流量实现精准流控,在保证核心业务调用不被限流的情况下满足大部分人的需求。

例如下图所示,在入口应用对不同的流量进行染色分组,可以给需要保障的核心重要接口添加 vip 标签,对非重要接口添加 normal 标签。服务 A 可根据不同分组流量来实现差异化的限流策略,对 normal 限制 20%的极限 QPS,对 vip 流量限制 80%的 QPS。在服务 B 中可以根据不同分组流量的重要性程度,保障 vip 流量在服务遇到非致命的错误时,可以通过重试的方式避免流量的最终调用失败,对错误率容忍程度更高的 normal 流量则不配置流量重试策略。


图 – 利用 Sermant 来实现基于标签的流控的过程


当前版本的 Sermant 也包含了流控插件,该插件支持限流、熔断、隔离仓、错误注入与重试、熔断指标采集、系统规则、系统自适应流控能力,并且支持配置中心动态配置规则,实时生效。上图的场景可挂载标签路由插件(提供染色能力)、流量标签透传插件以及流控插件来做实现。使用方式如下:

(1)标签路由插件配置流量的染色规则,比如针对不同账户的流量添加 x-sermant-group:vip,x-sermant-group:normal 标签。(该步骤可选,可用 API 网关等其他方式染色)

(2)标签透传插件配置透传规则,比如 x-sermant-group 键的标签在全链路中进行透传。

(3)流控插件配置流控规则,比如 header 中携带 x-sermant-group:vip 的流量在服务 B 中做相应的重试策略,x-sermant-group:normal 的流量在服务 A 中配置限流策略。

五、总结

Sermant 将流量染色和流量标签透传的能力以插件化的形式整合起来,形成了一套完整的全链路流量标签透传的解决方案。流量标签本质上是将流量进行分组,基于这套解决方案,我们可以使得流量标签遍布于整个流量拓扑中,在全链路灰度发布、限流降级、故障诊断恢复、负载均衡、流量监控等场景中发挥重要作用。此外,Sermant 的插件化架构还可以支持用户单独使用流量染色能力或流量标签透传能力,以积木化的使用方式自由组合各个插件模块。Sermant 的非侵入特性也能够让用户做到 0 代码修改地一键接入全链路流量标签透传能力。


Sermant 作为专注于服务治理领域的字节码增强框架,致力于提供高性能、可扩展、易接入、功能丰富的服务治理体验,并会在每个版本中做好性能、功能、体验的看护,广泛欢迎大家的加入。



发布于: 刚刚阅读数: 5
用户头像

华为云开源官方博客--携手共建云原生根社区 2023-03-13 加入

还未添加个人简介

评论

发布
暂无评论
流量治理的基石——基于字节码增强的全链路流量标签透传_微服务_华为云开源_InfoQ写作社区