在使用 jcci 项目后,发现了一些功能其实可以更加完美,通过传入项目 git 地址 分支 两次的 commit id,即可分析出两次 commit id 之间代码改动所带来的影响,并生成树图数据方便展示影响链路(README 原话)通过解析 cci 文件,得知具体受影响的 API,但无法得知具体是哪个微服务下的 API 受影响了,也不知道具体 API 名称,为此需要对这个进行完善,做到精准回归测试
先看一下流程图,我们是以部署节点作为最新的 commit id,与本地的 commit id 作为比较分析,譬如刚开始初始化克隆项目后,去到项目根目录执行git rev-parse HEAD
即可获取当前的 commit id,当开发在流水线平台操作部署时,通过 JenkinsFile 脚本,即可获取最新的 commit id,再调用执行分析 api,即可分析出此次需要回归的接口(图中红色框则需要完善的功能,jcci 分析直接调包即可调包侠 )
上图,我们可以得知有以下关系,一个 Git 项目维护着好几个微服务应用,mh-oms-parent
项目下面有 3 个微服务应用(mh-oms-admin
、mh-oms-biz
、mh-oms-pay
、mh-oms-user
)需要把各个微服务的接口文档数据同步下来,这里需要把解析 swagger 文档数据,再将解析后的数据进行入库,支持手动页面同步 + 定时器补偿(这里就不分享源码,都是些 curd 没意义,倒是可以看看库表设计,有兴趣可以问问我相关的细节)
# api项目表(微服务维度)
CREATE TABLE `api_project` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`business` varchar(64) NOT NULL COMMENT '业务线: MH_BUSINESS_TYPE',
`project_name` varchar(64) NOT NULL COMMENT '项目名',
`git_project_name` varchar(64) NOT NULL COMMENT 'git项目名',
`description` varchar(64) DEFAULT NULL COMMENT '项目描述',
`owner_name` varchar(32) NOT NULL COMMENT '负责人',
`owner_code` varchar(32) NOT NULL COMMENT '负责人编码',
`app_name` varchar(32) NOT NULL COMMENT '服务名',
`create_code` varchar(20) NOT NULL COMMENT '创建人编码',
`create_name` varchar(20) NOT NULL COMMENT '创建人',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_code` varchar(20) DEFAULT NULL COMMENT '更新人编码',
`update_name` varchar(20) DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`del_flag` smallint(6) NOT NULL COMMENT '0: 未删除 1: 已删除',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
# 数据源表
CREATE TABLE `api_project_source` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`project_id` int(11) NOT NULL COMMENT '关联的项目id',
`source_name` varchar(64) NOT NULL COMMENT '数据源名称',
`source_format` smallint(6) NOT NULL COMMENT '数据源格式: TOOLS_DOC_TYPE',
`source_url` varchar(255) NOT NULL COMMENT '数据源URL',
`path_prefix` varchar(32) DEFAULT NULL COMMENT '路径前缀',
`enable` tinyint(1) NOT NULL COMMENT '是否启用',
`import_rate` smallint(6) NOT NULL COMMENT '导入频率,业务字典:TOOLS_DOC_IMPORT_RATE',
`state` smallint(6) DEFAULT NULL COMMENT '运行状态,业务字典:TOOLS_DOC_RUN_STATE',
`last_import_time` datetime DEFAULT NULL COMMENT '上次导入时间',
`create_code` varchar(20) NOT NULL COMMENT '创建人编码',
`create_name` varchar(20) NOT NULL COMMENT '创建人',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_code` varchar(20) DEFAULT NULL COMMENT '更新人编码',
`update_name` varchar(20) DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`del_flag` smallint(6) NOT NULL COMMENT '0: 未删除 1: 已删除',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
# 目录表
CREATE TABLE `api_directory` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`project_id` int(11) NOT NULL COMMENT '项目id',
`parent_id` int(11) DEFAULT NULL COMMENT '父id,为空就是根目录',
`name` varchar(128) DEFAULT NULL COMMENT '目录名',
`type` smallint(6) NOT NULL COMMENT '目录类型: 1:api_object_data, 2:sql_object, 3:dubbo_object, 4:case_object, 5:suite_object',
`index` int(11) NOT NULL COMMENT '目录排序',
`create_code` varchar(20) NOT NULL COMMENT '创建人编码',
`create_name` varchar(20) NOT NULL COMMENT '创建人',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_code` varchar(20) DEFAULT NULL COMMENT '更新人编码',
`update_name` varchar(20) DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`del_flag` smallint(6) NOT NULL COMMENT '0: 未删除 1: 已删除',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8mb4;
# api对象数据表
CREATE TABLE `api_object_data` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`project_id` int(11) NOT NULL COMMENT '项目id',
`directory_id` int(11) DEFAULT NULL COMMENT '目录id',
`name` varchar(128) NOT NULL COMMENT '接口名称',
`description` varchar(256) DEFAULT NULL COMMENT '接口描述',
`tag` varchar(256) DEFAULT NULL COMMENT '接口标签',
`base_url` varchar(256) DEFAULT NULL COMMENT '接口域名',
`base_path` varchar(128) NOT NULL COMMENT '接口路径',
`method` varchar(32) NOT NULL COMMENT '接口请求方法',
`header` text COMMENT '接口请求头',
`query` text COMMENT '接口查询参数',
`body` text COMMENT '接口请求参数',
`body_type` smallint(6) NOT NULL COMMENT 'body类型: 0: none 1: json 2: form 3: x-form 4: binary 5: GraphQL',
`response` text COMMENT '接口返回参数',
`create_code` varchar(20) NOT NULL COMMENT '创建人编码',
`create_name` varchar(20) NOT NULL COMMENT '创建人',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_code` varchar(20) DEFAULT NULL COMMENT '更新人编码',
`update_name` varchar(20) DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`del_flag` smallint(6) NOT NULL COMMENT '0: 未删除 1: 已删除',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=171 DEFAULT CHARSET=utf8mb4;
这里需要维护 Git 项目的相关信息,主要为 git url、branch、git token 等
# Jcci项目表
CREATE TABLE `jcci_git_project` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`project_name` varchar(64) NOT NULL COMMENT '项目名称',
`description` varchar(64) DEFAULT NULL COMMENT '项目描述',
`git_project` varchar(64) NOT NULL COMMENT 'git项目名',
`git_url` varchar(256) NOT NULL COMMENT 'git地址',
`git_branch` varchar(32) NOT NULL COMMENT 'git分支名',
`git_token` varchar(256) NOT NULL COMMENT 'git token',
`local_commit_id` varchar(256) DEFAULT NULL COMMENT '本地提交id',
`status` smallint(6) NOT NULL COMMENT 'jcci项目状态: jcci_project_status',
`create_code` varchar(20) NOT NULL COMMENT '创建人编码',
`create_name` varchar(20) NOT NULL COMMENT '创建人',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_code` varchar(20) DEFAULT NULL COMMENT '更新人编码',
`update_name` varchar(20) DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`del_flag` smallint(6) NOT NULL COMMENT '0: 未删除 1: 已删除',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
重点说一下第二步,如何改造 jcci 的代码
主要用的是 jcci 项目中的analyze_two_commit
方法,里面_can_analyze
方法存在sys.exit(0)
需要将其抛出异常或者直接 return,具体微改造如下:
analyze_two_commit
方法改造如下:
嗯,改动点不是很多,调包完事,分析完,只要解析分析结果数据就好了
使用 Gunicorn 部署时,Gunicorn 可以指定 worker 参数,指的是开启的进程数,项目启动时,每次开一个 worker,都会启动一个 scheduler,这就导致了这些定时任务是由不同的进程创建的。
解决方法:
使用 Redlock(redis 分布式锁)进行定时任务上锁
def lock(key: Union[str, Callable], ttl: int = 3000):
"""
redis分布式锁,基于redlock
:param key: Redis key
:param ttl: 锁释放时间
:return:
"""
def decorator(func):
def wrapper(*args, **kwargs):
try:
redis_key = key(*args, **kwargs) if callable(key) else key
if not redis_key:
raise BusinessException('缺少redis key无法上锁操作!')
# 锁释放时间为30s
with RedLock(redis_key, connection_details=Config.REDIS_NODES, ttl=ttl):
return func(*args, **kwargs)
except RedLockError:
appContextRepo.logger.error(f"redis key 为 {redis_key}")
appContextRepo.logger.error(f"进程: {os.getpid()}, 执行函数{func.__name__}失败, 不用担心, 还有其他哥们给你执行了")
raise RedLockException("操作太频繁了, 请稍后再试!!!")
return wrapper
return decorator
这里意思是运行的时区和系统时区不匹配
解决方法:
线上环境是 docker 容器,进行 cat /etc/timezone,显示的是 Etc/UTC,解决的思路是修改 Dockerfile,配置正确的时区,在 Dockerfile 中加入此行配置即可
将发送版本变更接口影响报告集成到流水线上,发现获取的提交信息和提交作者出现了换行,导致解析 json 失败
解决办法:
通过replace('\n','')
将换行符替换掉
原本脚本上设定的超时时间为 2s,如果影响接口比较多的情况下,接口处理会比较慢,导致超时
解决方法:
将接口设置成异步接口 (直接用BackgroundTasks
)
新增一个节点(作为分支节点,不影响原来部署流程),编写 curl 脚本,触发影响分析报告(异步生成报告,不影响原有流水线的构建速度)
# 获取提交人和提交信息
commit_author = sh( script:"""echo `git log -1 --pretty=tformat:"%cn" `""", returnStdout: true)
println commit_author
commit_text = sh( script:"""echo `git log -1 --pretty=tformat:"%s" `""", returnStdout: true)
println commit_text
try{
timeout(2){
def wxstdout = sh script:"""
curl --location --request POST 'http://192.168.240.110:30080/api/tool/application/send' \
--header 'Content-Type: application/json' \
--data-raw '{
"project": "mh-oms-parent",
"commit_id": "${env.GIT_COMMIT}",
"commit_text": "${commit_text.replace('\n','')}",
"commit_author": "${commit_author.replace('\n','')}",
"branch": "${env.GIT_BRANCH}",
"key": "xxxxxxxxxxxxx"
}'
""", returnStdout: true
println wxstdout
}
} catch (Exception e){
println e
echo '触发异常2!'
}
}
🏄 **项目:**<font color="grey">{project}</font>
🏁 **Commit_Id:**<font color="grey">{commit_id}</font>
📖 **分支:**<font color="grey">{branch}</font>
🙎🏻 **代码提交人:**<font color="grey">{commit_author}</font>
🚀 **代码提交信息:**<font color="grey">{commit_text}</font>
⏰ **发送时间:**<font color="grey">{now_time}</font>
**影响情况:**
报告效果
感谢 jcci 作者,写出这么棒的项目!@ 白开水 pp
此次只是实现方案分享,没有源码,没有源码,没有源码(一大堆 curd 的代码,分享了也没意义);大家且看且珍惜,毕竟现在技术 xxxx,有兴趣可以问问我相关的细节,欢迎大家交流👏🏻👏🏻👏🏻