likes
comments
collection
share

有关于流量染色的一些实践

作者站长头像
站长
· 阅读数 12

项目背景

在实际项目中,综合成本考虑,我们一般会提供4套环境:开发、测试、预发、线上,用于满足我们日常的开发、测试、灰度需求。理想情况下,这是够用的。但在需求快速迭代的场景下,就会暴露出一些问题:

  1. 需求快速迭代,极有可能会存在 同一个服务多个需求并行开发的情况。此时该如何保证测试环境的稳定性?
  2. 本地开发时将服务注册到注册中心,即影响别人的测试,也影响自己的功能调试。该这么解决?

可以通过扩展环境来解决这个问题,即:一个环境不够用,就多部署几套环境。但这种方式有非常明显的问题:

  1. 资源成本太高:扩展一套环境,除了服务本身,往往还依赖很多基础组件:注册中心、配置中心、消息 等。
  2. 沟通成本太高:服务调用链路问题。想要服务是可用的,就需要保证整条服务链路是可用的。比如说RPC调用,A依赖B,B依赖C......最后可能就是就是全量部署。
  3. 管理混乱:一个环境在使用之后,很有可能在未来很长一段时间都不会再使用,如果这时候涉及到人员变动,这会让新人非常困惑。
  4. 因为服务的依赖关系,一些基础服务只能被动部署,最终只会越来越混乱。

有关于流量染色的一些实践

另一种解决方案就是流量染色,我们之前叫做环境隔离。即:在元数据中心,维护每个环境对应的服务列表;在流量的入口处,对请求添加标识;在基础框架层,对流量标识进行解析、透传 和 服务路由。此时的环境将变得非常轻量级,并且不存在物理隔离的概念,仅仅是一份服务列表元信息的维护。

流量染色

在元数据中心,维护每个环境对应的服务列表;在流量的入口处,对请求添加标识;在基础框架层,对流量标识进行解析、透传 和 服务路由。

在我理解中,流量染色主要就是要实现这几个目标:

  1. 降成本
  2. 测试提效
  3. 环境治理,可控

在介绍下面的内容之前,我们先定义两个规则:

  1. 一个服务在注册时,如果没有带上流量标,我们则认为当前服务实例是该服务的稳定分支;否则为 tag分支
  2. 每个服务,最少要保证部署一个稳定分支

下图是一个链路为 A -> B -〉C 的RPC调用。假如该链路同时过来两个需求,每个需求涉及到的服务改造不同:

  1. tag1: 涉及到 A 和 C的改造
  2. tag2: 涉及到 A B C 的改造
  3. 稳定分支,可能还会被其他服务调用,保持不变。

有关于流量染色的一些实践

综合成本考虑,我们希望按上图的请求链路进行路由:

  1. 如果一个请求标识中没有带上tag,则认为他是一个稳定分支的请求,此时保证所有请求都路由到各服务的稳定分支
  2. 如果一个请求代理流量标tag1,因为tag1 只涉及到 A C的改动,B服务没必要部署,未来节约成本,我们希望请求A C 的tag1分支,B的稳定分支
  3. tag2涉及到 A B C的改造,所以对于tag2的请求,需要保证 请求 A B C的tag2分支

以上是一个RPC调用的例子,其他场景也是类似,比如:

  1. RPC调用:流量带有tag1,表示希望调用到tag1的服务
  2. 消息:消息带有tag1,表示希望被带有tag1标识的消费端消费
  3. 分布式任务:之前的分布式任通过消息实现,和消息的实现是一致的。

常见问题

上面介绍的可以看作是流量染色的运行效果,要实现它,我们还涉及到很多东西的改造,比如:环境维护、服务打标、元信息维护、流量打标与透传、测试插件、基础框架改造。

除了基础框架的改造,其他的改造其实可以看成是环境/流量治理方面的一些事情。因为有了这一套基础环境之后,按理来说之后可以满足于任何基础框架。比如我们之后换了一套RPC框架,只需要针对新的RPC框架做一次改造,就可以融入到现有的这套流量染色体系中。

有关于环境维护

这里的环境可能和我们日常接触的环境不太一样。这里的一个环境其实可以看成是一个tag,即:一个测试场景。所以这种环境必然更加的灵活,可以动态的新增和删除。 每个环境对应着一个唯一的tag标识。在实际测试中,根据需求,透传对应的tag标识。

有关于服务打标

一个服务在部署的时候,需要知道它当前属于哪个环境(tag)。一般的做法是发布平台添加启动参数,框架层解析启动参数执行对应操作,比如:RPC服务解析tag并将其注册到注册中心;消息生产时添加消息tag,消费时进行对比。所以这部分涉及到发布平台的改造

有关于元信息维护

元信息:即维护某个环境中,涉及到改造服务的服务列表。为什么要维护这个关系,有几个好处:

  1. 链路直观:针对某个需求,测试人员可以直观的看到当前需求涉及到哪些服务的改造,这个服务列表可以由、产品、开发、测试共同创建。这样也有利于从全局上把控本次需求的风险。
  2. 测试提效:可能存在这样一种场景,A(tag1) -> B(tag1) -> C(tag1),假如C(tag1)因为测试环境OOM原挂掉了,那此时请求链路会变成 A(tag1) -> B(tag1) -> C,这就会对测试产生干扰。有了元信息,我们就可以做一层判断,当一个服务的tag分支需要存在,却没有部署或意外挂掉时,需要给客户端一写异常提示。也方便开发排查问题。

在我接触过的流量染色项目中,其实是将 环境维护 、 服务打标 、元信息维护 统一维护的。元信息其实和环境相关;服务打标依赖发布平台;发布平台要对服务打标依赖环境信息。所以这三者其实可以统一发布平台进行维护,其实可以看成是流量染色这种环境在发布平台上的支持。例如:

有关于流量染色的一些实践

以上只是一个简单的示例图,实际肯定会有跟过提升体验的操作。比如:结合git,实现tag分支的动态创建和合并管理。

有关于元信息的存储与获取

存储:元信息主要是给各基础框架使用的,根据元信息来判断我们的路由规则,比如:RPC框架、消息框架等。所以此时我们要保证元信息的一个动态感知。可以使用配置中心,比如:ZK、Nacos。

获取:考虑到很多基础框架都会用到这份元信息,所以可以针对元信息封住一个SDK,也方便后续的改动与升级

有关于流量打标与解析透传

流量打标:一般在流量的发起发方对流量进行标记,一般做法是对该请求添加一个请求头,将tag信息放到请求头中。

解析透传:将请求头的tag解析出来,将其透传到基础框架的请求上下文中。

比如有这样一条链路: http ==> api网关 ==> dubbo:此时测试人员在http请求头中添加tag标识,api网关解析该请求头,将其透传至dubbo的 RpcContext 中。

如果是没有通过网关访问呢? 比如:域名 ==》 转发 ==〉 web服务 。,这里面涉及到几部分改造:

  1. 转发阶段:能够识别请求头,然后转发到正确的实例。
  2. 转发实例之后,客户端解析请求头,透传至框架的 请求上下文。

同理,考虑到当亲请求流量标的获取会在很多基础框架中用到,可以将这部分内容封装一个SDK

有关于测试插件

现在有一个问题:测试人员如何在请求头中添加一个环境tag呢?他们可能并不懂开发;或者很多时候都是直接在管控台上进行测试的,此时该如何透传这请求头呢?

可以开发对应的插件,比如: chrome插件。通过可视化的方式添加请求头,非常方便

有关于基础框架改造

实现方式可能有多种,以我接触过的为例:

  1. Dubbo:添加路由、透传tag
  2. kafak:发消息的时候带上tag,消息广播,消费端判断
  3. 分布式任务:基于消息实现,原理和消息一致
  4. 本地任务修改库数据:业务方自行决定数据偏移。当前节点的tag可知,业务方自己保证某个tag对应的数据范围
转载自:https://juejin.cn/post/7087078047538479141
评论
请登录