> 作者:李来 华为云高级软件工程师
一、全链路流量标签透传
在微服务架构中,流量标签用于对流量进行标记和分类,可能在微服务之间实现更精密的路由、负载平衡和流控等流量治理能力。以 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 作为专一于服务治理畛域的字节码加强框架,致力于提供高性能、可扩大、易接入、功能丰富的服务治理体验,并会在每个版本中做好性能、性能、体验的看护,宽泛欢送大家的退出。
- Sermant 官网:https://sermant.io
- GitHub 仓库地址:https://github.com/huaweicloud/Sermant
- 扫码退出 Sermant 社区交换群