中通动态多环境落地实践

随着公司(中通)业务规模不断地扩大,需求更新迭代速度加快。如何为业务线提供快速、持续、按需发布的能力成为了首先要解决的问题。本文主要阐述在持续交付的过程中,如何通过一键拉起多套测试环境来为业务团队赋能的实践。

图片

在按需发布的落地过程中,多版本并行的现象越来越频繁。以下是在日常迭代过程中,我们通常会遇到的问题:

1. 测试环境无法并行测试

我们经常遇到,产品经理提了一个紧急需求,当天要上线,但测试环境都被现有版本占用,只能按优先级排队部署测试上线,大大降低了交付时效。测试环节成为项目并行度的主要瓶颈,单一测试环境,无法做到并行测试,导致效率不高,按需发布功能不能真正实现。

2. 测试环境不稳定

多个业务线产品集成测试时,发现中间某个产品的环境时好时坏,原因有人在联调期间不断有其它版本部署该环境。导致项目测试时断时续,对测试而言缺乏沉浸式体验。

3. 测试环境被抢占

由于开发环境不完整和不稳定,不少开发同学转而寻求端到端的线下联调或者抢占测试环境联调,影响测试进度。

通常业务线团队为了提升交付速度,会同时并发多个版本。但固定的测试环境资源,由于无法隔离、不稳定、不够用等原因,常常拖慢上线进度,显然无法满足快速迭代的需求。

图片

致力于为业务线提供快速交付的能力,平台团队结合中通整个业务链路的特性,以发布平台为切入点、打通了网关的流量调度与容器的资源调度,形成的一套为业务产品线提供高效、稳定、按需发布的测试环境。围绕着动态生成的tag访问机制实现了Dubbo、MQ、ZDTP(数据同步),任务调度的环境流量隔离,具体方案如下:

图片

流量隔离原理图

图片

整体使用流程图

此方案主要基于应用级的流量隔离,不涉及数据库、Redis等其它中间件,具体说明有以下几点:

● 整个动态多环境內多个应用/服务以tag为路由,有tag的走tag路由,没有tag的走base兜底。

●【Dev】【Fat】环境逻辑上就是一组特定的tag,线下环境数据和中间件共享。

● 动态多环境中跨项目业务依赖的发版需要约定统一tag(发布平台提供)线上和线下环境数据和中间件物理隔离。

 核心流量隔离策略 
1. Dubbo路由

基于Dubbo原生的静态tag标签路由机制,通过将某一个或多个服务的提供者划分到同一个分组,约束流量只在指定分组中流转,从而实现流量隔离的目的。

● 首先需要一套部署所有应用的稳定兜底环境,即base环境,没有打tag的环境,其次每次动态创建的版本自动打上版本的tag,让稳定环境与新创建的机器都注册到一个注册中心。

● 用户通过动态前端域名访问后端接口时,调用网关http接口时解析URL中的refer并将tag加入header中,当网关调用Dubbo服务时自动将tag写入RpcContext中的 dubbo.tag保证路由的正确性。Dubbo层,当服务被调用时,会通过AbstractClusterInvoker 类的invoke方法发起调用,此时会将RpcContext.getContext().getAttachments()的值set到invocation上,然后将invocation作为参数获取匹配到的Provider端提供的方法。在获取服务方提供者时,Router类按规则过滤出服务提供者的子集。针对TagRouter,在同一进程服务消费的请求中invocation包含了tag的信息,如果提供方没有完全匹配的服务则进行服务降级流量达到base环境对应的服务。

图片

图片

图片

Dubbo流量隔离图

图片

2. MQ路由

在多环境下,每个版本都会携带动态tag。

● 期望实现:版本V1发送者发送的消息,只希望同为版本V1的消费者消费到,同样希望消费发送者V2发送的消息只被同为版本V2的消费者消费。

● 实际运行情况:版本V2的消费者可以消费到版本V0、V1、V2的消息,造成消息在各个版本之间的混乱,影响多版本并发测试。

● 方案原型:

图片

● 设计思路: 消费者或生产者在启动时会向zk注册各自的客户端信息,通过注册到不同的路径来区分版本消费者与Base消费者。

图片

● 主要实现要点:

1.无标签消费组,下文统称为默认消费组,应用启动时,会向zookeeper的/consumegroup/{consumergroup}/mqtag/empty路径下注册连接信息。

2.默认消费组客户端在启动时,监听 /consumegroup/{consumergroup}/mqTag 节点,实时感知标签消费者的上线与下线,并将这些信息同步缓存在本地,方便后续在消费时,用于判断消息tag是否有匹配的消费者在线。

3.带有标签的消费者启动时,首先向 /consumegroup/{consumergroup}/mqTag/{tag}下注册自己,标记消费者上线,供默认消费组识别对应标签是否存在,并且带标签的消费组,订阅的主题为 标签_主题_消费组

4.默认消费组在消费消息时,首先解码消息的tag属性,如果为空,则消费,如果不为空,则结合本地缓存中该tag是否有在线消费者
1)如果没有,则消费
2)如果有对应的tag在线,则将消息转发给主题:标签_主题_消费组

5.MQ多环境接入时,通过发布平台服务(消费者,生产者)启动时参数均携带参数-DzmsRouteTag,挂载javaagent实现动态多环境隔离。

● 技术方案:

1. 技术实现主要涉及字节码插桩技术

2. MQ消息隔离功能主要基于ZMS-SDK(中通自研消息中间件)进行字节码插桩,拦截相关方法实现动态多环境消息隔离方案

3. 主要功能清单

1)ZMS-SDK消费者扩展ZK注册信息, 支持版本和base区分, 实现消息转发能力

a)对消费者元数据信息进行判断/重置和注册路径的更新与扩展(实现BASE/版本消费者的上下线)

b)对ZM-SDK消费消息并回调应用业务逻辑的实现类进行扩展(实现消息的转发或消费

c)版本消费者监听BASE消费者的注册情况, 在BASE掉线的情况下,支持BASE/版本自动升降级,最大程度保障消息转发的稳定性

2)ZMS-SDK生产者支持自动添加mqTag

a)对生产者注册信息进行扩展,支持识别版本或BASE生产者

b)如果当前是版本生产者, 自动在消息头中添加mqTag

c)如果上下文中存在动态多环境Tag, 也会在消息头中添加mqTag(可以衔接dubbo tag 或其他指定tag逻辑,主要用于dubbo-mq/mq-dubbo等业务场景)

3)MQ路由表

a)用于消息生产端无法添加mqTag的场景, 可以配置无mqTag的消息指定版本消费

● MQ动态多环境流程介绍

图片

1.发送者拦截逻辑

a.Agent上下文中存在tag, 则进行消息头的设置

b.Agent上下文中不存在,则从System.getProperty中获取, 如果是版本生产者,则可以从环境中获取对应的tag

c.内部添加zmsRouteTag实现案例:

图片

消费者进行转发或消费逻辑

a.在consumer base 消费者处理消息时, 会获取消息头中的mqTag进行逻辑处理

i.存在mqTag, 但对应版本消费者不在线,  base消费

ii.存在mqTag 并且对应版本消费者在线, 消息转发到对应版本的topic中

iii.不存在mqTag, MQ路由表中也不存在路由信息, bas消费

iv.不存在mqTag,但MQ路由表中配置了该应用的消费路由信息, 则根据路由表进行转发(也会判断路由表配置的版本消费者是否在线, 在线才会转发)

b.consumer消费时如果存在mqTag还会设置Agent上下文, 用于将动态多环境的tag透传下去(如衔接dubbo, dubbo consumer请求前也会获取Agent上下文中的tag)

c.转发逻辑代码简述:

图片

 Dubbo Tag和MQ Tag相互透传

dubbo tag与mq tag相互透传的实现基于动态多环境Agent的AgentContext

主要场景流程简述如下:

1.mq消费数据, 在mq消费逻辑中进行dubbo调用, 需要将mq消息头中的透传给dubbo tag

a.Agent在mq消费时会将消息头中的mqTag 设置到Agent上下文 AgentContext中

b.dubbo consumer时会从AgentContext中获取, 如果存在tag则会设置成dubbo tag

主要代码逻辑简述:

图片

图片

2.dubbo provider时会生产数据到mq, 需要将该dubbo tag 透传给mq

a.Agent会将dubbo的RpcContext中的duubo tag设置到Agent上下文的AgentContext中

b.mq发送数据前会从AgentContext中获取tag, 如果存在tag则会设置成mqTag

主要代码逻辑简述:

图片

图片

MQ流量隔离图

图片

根据以上方案从第二季度开始在业务线试点落地,刚开始对接业务产品时并不顺利,主要遇到以下几个问题:

1. 链路中未开多环境的Dubbo和Http流量如何调度?

在实际链路中,由于上游未开通、外部第三方的http调用等调用时无tag将影响到环境隔离,主要分两种情况:

● 上游调用当前服务的Dubbo接口无tag。

● 上游调用当前服务的Http接口无tag。

优化方案:

此种情况 ,下游服务部署base环境解决,双方都在无tag环境中调试。因此base从不开放部署权限,改成了按需配置部署权限。

2. 链路中未开多环境的MQ消息流量如何调度?

现实中,经常会有上下游联调的场景,多环境的版本在修改消息业务逻辑后,消息消费时希望指到当前的版本。

优化方案:

关于MQ的路由,在同个产品内的消息provider和consumer默认拥有多环境的路由机制。只有消费外部发出的消息时,我们希望能被指定版本的consumer进行消费。因此,提供了路由按需配置。同一时间配置了路由的消息,会先由base根据规则进行转发,当找到配置了路由的consumer时将由指定的版本进行消费。否则base自己兜底。

图片

图片

3. 代码中Dubbo filter清除了tag

多环境对接在进行流量验证时,明明调用服务的入口已设置dubbo.tag,但tag一直是空的流量一直打到base,经过链路排查项目代码中实现了Dubbo的filter并且进行了dubbo调用,会清除tag,导致路由失败,如下图。在多环境中主要以tag为路由标识,必须禁止清除tag的操作。因此多环境中应避免这种写法。

dubbo 中清除tag的源码如下:

图片

案例代码如下:

图片

4. 版本中新增的consumer没有base无法路由的问题

根据原来MQ多环境agent的逻辑只实现了已有的consumer,对于新增的consumer此时还不存在base,没有转发能力无法路由。

优化方案:

应用启动时,新增的consumer被注册到zk后,agent巡检base是否存在,不存在的自动升级为base且会再创建一个带tag的consumer。确保任何情况下都能完成路由转发的能力。

5. Spring MVC http流量路由问题

有些项目的服务既存在Dubbo服务又存在部分MVC服务,多环境默认支持Dubbo,一开始对http的流量隔离并不支持,导致链路中流量无法彻底隔离。

优化方案:

发布平台部署时传tag给容器平台 ,容器平台部署成功后,将IP与tag映射传给网关,当流量请求到网关层时做tag路由隔离。核心代码如下:

图片

流量调度简图

图片

6. 移动端动态路由问题

公司部分项目有嵌入在移动端的小程序和H5应用, 在移动端每个嵌入的应用都会先配置一个固定的访问域名即base环境的域名。而多环境则是每次生成动态的域名,显然移动端要支持多环境就需要改造。为了彻底解决多环境在移动端无法路由的问题,平台部开发了移动框架SDK来支持。由于原生和小程序前端资源打包时就已加载到客户端无法支持多环境的隔离。那么移动框架SDK能支持的动态路由的场景有哪些?

原生App和小程序:通过配置接口请求 header

既然前端资源无法动态隔离,那么我们可以通过配置接口请求 header来进行后端服务的路由。应用发出请求时,SDK拦截并通过修改header添加tag,网关层解析tag并进行路由调度。

H5:通过修改域名配置

在动态多环境下,每个版本的前端域都是按一定规则动态生成的。嵌入在移动端的H5应用实际访问时请求的是远程服务器的资源,可以通过修改H5原来的固定访问域名改到多环境的版本带tag的域名,应用发出请求时,SDK拦截请求根据用户修改的版本带tag的域名进行调用 ,此时流量进入网关层解析tag并完成路由调度(Dubbo和Http)。

图片

图片

从年初开始至今,多环境落地覆盖业务线链路核心涉及产品约200+个,目前还有产品在持续对接中,已对接多环境的产品需求交付发版数量约提升在50%左右。

复盘我们之前的三个痛点,测试环境不稳定、测试环境被抢占、测试环境无法并行的问题已基本解决。在多环境落地过程中,也遇到和解决了很多问题,如前端域名标准化落地、网关统一标准化落地、业务产线个性化多环境治理等。

今年聚焦更多的是单个产线的多环境提效。未来我们将更专注为中通整体业务链路提供更丝滑的多环境,致力于提高为业务线提供快速、高效、持续交付的能力。

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:【文末自行领取】

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值