简介: 本文通过梳理项目环境生命周期内创建、部署、重启、删除等任务的特点后,去除了流程引擎对消息的依赖,使用分布式分片任务,分布式锁实现任务的分布式运行。通过使用工厂模式,责任链模式,以及领域驱动设计的思路对流程引擎进行重构。最终实现在环境数量翻数百倍以上的情况下,日常以及预发环境平均创建成功率达到 99% 以上,单个环境创建时间由数百秒以上降低至 100秒 以下。工单量每秒并行执行翻近百倍的情况下,系统异常率低于 1% ,单次任务执行时间均值降低 8x%。
引言项目环境是集团研发同学联调测试必不可少的平台型工具之一,其环境申请与释放动态灵活,环境间流量相互隔离,在开发和上线前的个人自测以及全链路联调场景下有着不可替代的重要作用。一个稳定易用的项目环境能极大地提高一线研发同学的测试体验,通过对环境简化抽象、屏蔽基础设施和微服务复杂性,为业务提供稳定可靠、简单易用的测试环境。
项目环境作为与变更或某个项目的生命周期保持一致的灵活环境,相较于固定环境(如主干,预发,正式)有着不同的特质,其环境申请和释放与变更生命周期绑定,部署和重启更是其中的高频操作。随着项目环境覆盖范围的推开,在每天开发高峰期时期,每秒同时运行的工单量(环境创建、部署、重启、删除等)最高能到数千余次。这为项目环境本身的稳定性提出了极高的要求。
在项目环境建设初期,每一次创建、删除、部署等工单主要通过单机执行,通过内部流程引擎发送消息以及定时任务推动状态流转。在实践过程中随着工单量的上涨逐渐出现了任务重复运行,任务无迹象消失,任务猝死等问题。而研发流程的改造和推进也给项目环境提出了更高要求,但是以上问题的出现限制了项目环境承载更大范围使用的可能性。
本文通过梳理项目环境生命周期内创建、部署、重启、删除等任务的特点后,去除了流程引擎对消息的依赖,使用分布式分片任务,分布式锁实现任务的分布式运行。通过使用工厂模式,责任链模式,以及领域驱动设计的思路对流程引擎进行重构。最终实现在环境数量翻数百倍以上的情况下,日常以及预发环境平均创建成功率达到 99% 以上,单个环境创建时间由数百秒以上降低至 100秒 以下。工单量每秒并行执行翻近百倍的情况下,系统异常率低于 1% ,单次任务执行时间均值降低 8x%。
技术实践文章架构现状1. 流程梳理
环境创建部署等流程如下所示,研发同学创建变更后项目环境会自动开始创建流程,非云原生应用在环境创建完成后便开始异步申请机器资源,等待资源扩容完成后,会自动开始部署流程。云原生应用会在部署过程中生产资源。项目环境资源生产完成后建立隔离标与机器的映射关系,即打标流程。三种工单(创建、部署、重启)的核心流程如下图所示:
2. 任务特点
可以看到上述三类任务有着两个显著特点:
- 任务间有顺序
- 单个任务为异步进行
异步任务需要在条件满足后才会触发下一个任务的执行,这是一个典型的流水线模型,所以我们内部将推动任务流转的模型称为流程引擎
3. 任务处理现状
异步任务由用户触发创建,定时任务会定时调用异步任务判断同步结果,任务完成后会通过消息推动任务间流转。
3.1 任务猝死
当异步任务在运行过程中抛出非预期的异常会导致任务直接猝死,由于没有发送任务完成的消息,任务间流转所依赖的消息丢失就导致流程引擎无法推动下一个任务运行。
3.2 任务处理单机瓶颈环境创建、部署、重启都属于不可重复任务,如果被定时任务被分布式调用,会导致任务被重复执行导致资源浪费,工单报错等无法预期的任务。而单机调度就导致项目环境大部分的机器被闲置,出现一核有难、八核围观的状况。
3.3 任务重复执行
任务量的增多还会导致任务被重复执行。为了更快地推动任务执行,定时任务的运行间隔必须设置得足够短。但是随着任务量的急剧增多,定时任务单机处理会导致当前批次处理的任务在下一次定时任务被调度时仍未执行,从而导致任务被重复执行。
优化之路1. 任务猝死优化
在流程推进过程中任意一个非预期异常都会导致任务戛然而止,在用户看来就是环境创建无机器,不部署,流水线不展示,隔离不生效等。首先对当前流程引擎的流程架构进行了初步梳理,如下图所示(可以不看,仅做展示)
1.1 架构升级-领域模型(DDD)
欲重构先架构,通过整理梳理发现,整个流程引擎可以分为四大部分,
- 触发流程创建的 GroupEnv(项目环境) 实体
- 流程操作的 AppRunningEnv(应用环境) 实体
- 保存流程信息的 Operation 实体
- 推动流程运行的 TaskEngine(流程引擎) 实体
根据抽取出来的四个实体,重新总结绘制了相互间的依赖关系图,将分散在各处的部署,删除,重启等依赖关系收敛到各自的 Domain 中,并抽取出四个能力:创建,提交运行,操作以及更新状态。
1.2 流程引擎重构1.2.1 执行器统一接口
可以发现,子任务的执行过程可以分为两个部分
(1)执行操作
(2)同步结果
其中执行操作需要拿到子任务的初始执行结果。同步结果操作需要同步外部系统的状态到项目环境并更新子任务结果。所以可以将这两个能力抽象到一个接口中,即Taskexecutor。对于不同的子任务调用不同的executor。
1.2.2 使用工厂方法分拆执行器针对不同操作使用不同的执行器这个思路与工厂模式非常契合。所以可以将每一个子任务执行器都抽取为一个单独的类,通过操作类型以及阶段从工厂类(ExecutorFactory)拿到对应的执行器执行对应的方法。
1.2.3 异常处理兜底在流程引擎execSubTask以及handleSubTaskSyncResult两个方法中做异常统一处理,规范执行器异常处理,在异常信息中携带第三方或己方处理结果并格式化后填充到异常信息中。
1.2.4 操作子任务状态统一透出在子任务执行器中构建统一的异常信息模板,并通过异常的形式透出给上层调用者。上层调用者不需要关注执行器中发生了什么,只需要将异常信息落库。
1.2.5 单元测试补齐以流程引擎为入口,以HTTPClient与mapper为最大mock对象,实现全链路测试。
1.3 优化结果重构后,极大地简化了任务执行流程,去除了对metaQ的强依赖,流程引擎依赖定时任务触发,实现异常兜底逻辑,单测全覆盖。共删除冗余代码2700行。流程引擎核心流程收敛至Domain。
点击链接查看原文,获取更多福利!
https://developer.aliyun.com/article/1050033?utm_content=g_1000361281
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。
,