
作者 | 肖清海
当前 DolphinScheduler 版本: 3.1.3 当前 seatunnel 版本: 2.1.3
部署规模: 1台Master + 2台Worker,工作流定义3700+,日均调度任务量20000+
使用年限: 已经稳定运行3年
不追求「逐步升级」,而是「换新环境+数据迁移」:
| 维度 | 官方升级方案 | 本方案(重建+API) |
|---|---|---|
| 停机时间 | 多次升级,累计停机可能数小时甚至是数天 | 切换时停旧任务启动新任务,基本是无缝衔接】 |
| 回滚难度 | 困难(数据库已变更) | 简单(旧环境完好,随时切回) |
| 数据一致性 | 需验证所有表结构迁移 | 只迁移核心业务数据(工作流定义),历史执行记录不迁移 |
| 版本适配 | 需处理中间版本的所有兼容性问题 | 直接适配3.4.1,只改造必要参数 |
| 工作量 | 需多次验证 | 主要集中在脚本开发 |
| 适用场景 | 小版本升级 | 大版本跨越、任务量大 |
3.1.1 新环境部署
3.1.2 API访问配置
| 核对事项 | 表 |
|---|---|
| 租户表 | t dstenant |
| 项目表 | t dsproject |
| 用户表 | t dsuser |
| 环境表 | t dsenvironment,t dsenvironment workergroup_relation |
| 工作组 | t dsworker_group |
| 数据源表。 | t dsdatasource |
3.3.1 前期准备测试
3.3.2 代码开发-读取原任务定义
...
//获取整个, 循环处理
String processDefinitionUrl = OLD_URL + "/dolphinscheduler/projects/" + oldProjectCode +
"/process-definition/query-process-definition-list";
Map<String, String> map = new HashMap<>();
map.put("projectCode", oldProjectCode);
String pdRes = httpClientUtilOld.doGetRequest(processDefinitionUrl, map);
ArrayList<JSONObject> dataList = parseResDataToList(pdRes);
for (JSONObject job : dataList) {
String oldWFCode = job.get("code").toString();
Map<String, String> mapPara = new HashMap<>();
String oldurl = OLD_URL + "/dolphinscheduler/projects/" + oldProjectCode
+ "/process-definition/" + oldWFCode;
mapPara.put("code", oldWFCode);
mapPara.put("projectCode", oldProjectCode);
String res = httpClientUtilOld.doGetRequest(oldurl, mapPara);
JSONObject jsonObject = JSON.parseObject(res);
JSONObject data = (JSONObject) jsonObject.get("data");
JSONObject processDefinition = data.getJSONObject("processDefinition");
JSONArray processTaskRelationList = data.getJSONArray("processTaskRelationList");
JSONArray taskDefinitionList = data.getJSONArray("taskDefinitionList");
//todo 获取 n个编码,替换旧任务编码
//填充 信息创建任务
createWF(processDefinition, processTaskRelationList, taskDefinitionList, NEW_IP, newProjectCode);
3.3.3 API创建新工作流
//看原工作流的任务有几个 获取任务编码
int taskCnt = taskDefinitionList.size();
List<String> taskCodeList = taskDefinitionList.stream()
.map(obj -> (JSONObject) obj)
.map(obj -> obj.getString("code"))
.collect(Collectors.toList());
try {
// todo 获取 任务编码
String taskCodeUrl = NEW_URL + "/dolphinscheduler/projects/" + newProjectCode + "/task-definition/gen-task-codes";
HashMap<String, String> taskCodeMap = new HashMap<>();
//需要创建n个任务
taskCodeMap.put("genNum", String.valueOf(taskCnt));
String codeData = httpClientUtilNew.doGetRequest(taskCodeUrl, taskCodeMap);
Object codes = JSON.parseObject(codeData).get("data");
JSONArray taskCodeArr = JSON.parseArray(codes.toString());
//根据实际任务 添加任务下游接收参数
for (int i = 0; i < taskDefinitionList.size(); i++) {
JSONObject logTask = (JSONObject) taskDefinitionList.get(i);
if (“判断逻辑”)) {
JSONObject taskParams = logTask.getJSONObject("taskParams");
JSONArray localParams = taskParams.getJSONArray("localParams");
// 构造 hiveAmount
JSONObject hiveParam = new JSONObject();
hiveParam.put("prop", "hiveAmount");
hiveParam.put("direct", "IN");
hiveParam.put("type", "VARCHAR");
hiveParam.put("value", "");
localParams.add(hiveParam);
logTask.put("taskParamList", localParams);
JSONObject paramMap = new JSONObject();
for (Object obj : localParams) {
JSONObject param = (JSONObject) obj;
paramMap.put(param.getString("prop"), param.getString("value"));
}
logTask.put("taskParamMap", paramMap);
....
// 替换必要参数 code 和seatunnel执行引擎参数
for (int i = 0; i < taskCodeList.size(); i++) {
String oldCode = taskCodeList.get(i);
String newCode = taskCodeArr.getString(i);
//替换编码
// 替换sea引擎:"SPARK" "start-seatunnel-spark.sh"
taskDefinitionListJsonStr = taskDefinitionListJsonStr
.replace("\"code\":" + oldCode + ",", "\"code\":" + newCode + ",")
.replace("\"engine\":\"SPARK\",", "\"startupScript\":\"start-seatunnel-spark.sh\",");
taskRelationListJsonStr = taskRelationListJsonStr
.replace("TaskCode\":" + oldCode + ",", "TaskCode\":" + newCode + ",");
locationsJsonStr = locationsJsonStr
.replace(oldCode, newCode);
...
}
Map<String, String> map = new HashMap<>();
map.put("taskDefinitionJson", taskDefinitionListJsonStr);
map.put("taskRelationJson", taskRelationListJsonStr);
map.put("locations", locationsJsonStr);
map.put("name", processDefinition.getString("name"));
map.put("tenantCode", "omm");//processDefinition.getString("tenantCode")
map.put("executionType", processDefinition.getString("executionType"));
map.put("description",
processDefinition.getString("description") == null ? "" : processDefinition.getString("description"));
map.put("globalParams", processDefinition.getString("globalParams"));
map.put("timeout", processDefinition.getString("timeout"));
String processDefinitionUrl = NEW_URL + "/dolphinscheduler/projects/" + newProjectCode + "/workflow-definition";
String processDefinitionRes = httpClientUtilNew.doPostRequest(processDefinitionUrl, map);
3.4.1 流程
3.4.2 迁移实施
按照流程,先找个任务少并且有代表性的项目(199个任务),迁移后试运行一段时间,测试稳定性运行一周没有问题。后续迁移完成50个项目,总共约3700个任务,耗时10天左右
| 序号 | 项目名 | 项目code | 任务数 | 处理进度 | 备注 |
| ------ |------ |------ | ------ |------ |------ |
| 1 | 项目1 | 13* | 199 | 0416完成 | |测试运行一周
| 2 | 项目2 | ... |... | ... |... |
| ... | ... | ... |... | ... |... |
| 50 | ... | ... |... | ... |... |
3.4.3 运行情况
目前已经运行了近1个月时间。以前调度延迟的问题:几乎是10秒到1分钟甚至更严重,现在几乎没有调度延迟;调度缺失实例的问题,目前也未发生。
目前一切运行良好,暂未发现问题,后续持续观察。
数据丢失风险:迁移过程中可能遗漏部分配置
兼容性问题:旧版本特有配置在新版本不支持
业务中断风险:切换期间可能出现调度延迟
4.2.1 回滚方案
4.2.2 数据备份
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70014275/viewspace-3171710/ ,如需转载,请注明出处,否则将追究法律责任。