当前位置:   article > 正文

Apollo 10 — adminService 全量发布

admintypeservice

目录

  1. UI 界面
  2. Portal 服务
  3. admin 服务
  4. 总结

1. UI 界面

1

2

3

2. Portal 服务

当我们点击上面的发布按钮的时候,调用的当然是 portal 的接口。具体代码如下:

  1. /**
  2. * 全量发布
  3. * @param appId SampleApp
  4. * @param env DEV
  5. * @param clusterName default
  6. * @param namespaceName application
  7. * @param branchName 分支/灰度名称
  8. * @param deleteBranch true
  9. * @param model {"releaseTitle":"20180716220550-gray-release-merge-to-master","releaseComment":"","isEmergencyPublish":false}
  10. * @return
  11. */
  12. @PreAuthorize(value = "@permissionValidator.hasReleaseNamespacePermission(#appId, #namespaceName)")
  13. @RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}/merge", method = RequestMethod.POST)
  14. public ReleaseDTO merge(@PathVariable String appId, @PathVariable String env,
  15. @PathVariable String clusterName, @PathVariable String namespaceName,
  16. @PathVariable String branchName, @RequestParam(value = "deleteBranch", defaultValue = "true") boolean deleteBranch,
  17. @RequestBody NamespaceReleaseModel model) {
  18. // 如果是紧急发布,但该环境不允许紧急发布,抛出异常
  19. if (model.isEmergencyPublish() && !portalConfig.isEmergencyPublishAllowed(Env.fromString(env))) {
  20. throw new BadRequestException(String.format("Env: %s is not supported emergency publish now", env));
  21. }
  22. // 合并主版本和灰度版本, 得到一个发布 dto
  23. ReleaseDTO createdRelease = namespaceBranchService.merge(appId, Env.valueOf(env), clusterName, namespaceName, branchName,
  24. model.getReleaseTitle(), model.getReleaseComment(),
  25. model.isEmergencyPublish(), deleteBranch);
  26. ConfigPublishEvent event = ConfigPublishEvent.instance();
  27. event.withAppId(appId)
  28. .withCluster(clusterName)
  29. .withNamespace(namespaceName)
  30. .withReleaseId(createdRelease.getId())
  31. .setMergeEvent(true)
  32. .setEnv(Env.valueOf(env));
  33. publisher.publishEvent(event);// 发送邮件
  34. return createdRelease;
  35. }

接口职责不多:是否符合紧急发布的数据校验,调用 Service, 发布“配置发布”事件(发送邮件)。

看看调用 Service 的过程,该方法称为 merge ,实际上就是合并灰度和主版本的配置。代码如下:

  1. public ReleaseDTO merge(String appId, Env env, String clusterName, String namespaceName,
  2. String branchName, String title, String comment,
  3. boolean isEmergencyPublish, boolean deleteBranch) {
  4. // 计算 changeSets
  5. ItemChangeSets changeSets = calculateBranchChangeSet(appId, env, clusterName, namespaceName, branchName);
  6. // 调用 admin 服务
  7. ReleaseDTO mergedResult =
  8. releaseService.updateAndPublish(appId, env, clusterName, namespaceName, title, comment,
  9. branchName, isEmergencyPublish, deleteBranch, changeSets);
  10. Tracer.logEvent(TracerEventType.MERGE_GRAY_RELEASE,
  11. String.format("%s+%s+%s+%s", appId, env, clusterName, namespaceName));
  12. return mergedResult;
  13. }

做了 2 件事情: 计算 change 集合,调用 admin 服务。很明显,计算 change 对于 protal 非常重要。

calculateBranchChangeSet 方法主要将灰度配置和主版本配置合并。

代码:

  1. private ItemChangeSets calculateBranchChangeSet(String appId, Env env, String clusterName, String namespaceName,
  2. String branchName) {
  3. NamespaceBO parentNamespace = namespaceService.loadNamespaceBO(appId, env, clusterName, namespaceName);// 父版本 namespace
  4. if (parentNamespace == null) {
  5. throw new BadRequestException("base namespace not existed");
  6. }
  7. if (parentNamespace.getItemModifiedCnt() > 0) {
  8. throw new BadRequestException("Merge operation failed. Because master has modified items");
  9. }
  10. List<ItemDTO> masterItems = itemService.findItems(appId, env, clusterName, namespaceName);// 主版本 items
  11. List<ItemDTO> branchItems = itemService.findItems(appId, env, branchName, namespaceName);// 子版本 items
  12. ItemChangeSets changeSets = itemsComparator.compareIgnoreBlankAndCommentItem(parentNamespace.getBaseInfo().getId(),
  13. masterItems, branchItems);// 得到 changeSet
  14. changeSets.setDeleteItems(Collections.emptyList());// 防止误删除,emm,灰度的内容并不是全量的,因此上面的计算有些问题,并且目前没有删除功能。所以这里可以置空。
  15. changeSets.setDataChangeLastModifiedBy(userInfoHolder.getUser().getUserId());
  16. return changeSets;
  17. }

步骤:

  1. 获取主版本的 namespace 详细信息,用于数据检验,id 赋值。
  2. 获取主版本的所有 item 配置,再获取灰度版本的所有 item 配置,注意,灰度版本的 item 只有其自身新增的和修改的配置,不是全量的(这将导致后面一个奇怪的现象)。
  3. 比较两者差异,得到 change 集合。
  4. 设置 deleteList 为空 —— 奇怪现象(灰度的内容并不是全量的,因此上面的计算有些问题,并且目前没有删除功能。所以这里可以置空, 并且防止误删除)。
  5. 设置修改人。

这里需要注意的是计算差异到底是怎么计算的,为什么后面有置空 deleteItem 的操作。

我就不贴全部的方法了,贴一下对删除操作有影响的代码:

  1. /** 比较,忽略空格,返回一个改变的 items */
  2. public ItemChangeSets compareIgnoreBlankAndCommentItem(long baseNamespaceId, List<ItemDTO> baseItems, List<ItemDTO> targetItems){
  3. // 忽略新增/修改 item 代码......
  4. // 处理删除,但这个逻辑似乎不对. 不过此类不知道数据来源,工具类没有问题.
  5. for (ItemDTO item: baseItems){// 主版本
  6. String key = item.getKey();
  7. ItemDTO targetItem = targetItemMap.get(key);
  8. if(targetItem == null){//delete// 如果灰度版本里没有,说明删除了.
  9. changeSets.addDeleteItem(item);// 添加进删除集合
  10. }
  11. }
  12. return changeSets;
  13. }

可以看到,这段代码里,循环主版本,逐个对比灰度版本,如果灰度版本里没有,就添加进 delete 集合,而我们知道,灰度版本的 item 只有修改的和新增的,这时,将导致误删除。

但这个工具类的计算是没有问题的,有问题的是外层数据的完整性。

因此需要在外面打个补丁:changeSets.setDeleteItems(Collections.emptyList());

好,计算完 changeSet,就要调用 admin 服务了,并且把 changeSet 传递过去,然后返回一个 release 对象,表示发布成功,并发布事件。

在分析 admin 之前,总结一下 protal 的流程:

1240

3. admin 服务

从 portal 的代码中,可以看到,调用的是 admin 的 updateAndPublish 方法接口,看看这个接口:
位置 : com.ctrip.framework.apollo.adminservice.controller.ReleaseController.java
代码如下:

  1. @Transactional
  2. @RequestMapping(path = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/updateAndPublish", method = RequestMethod.POST)
  3. public ReleaseDTO updateAndPublish(@PathVariable("appId") String appId,// 应用名称
  4. @PathVariable("clusterName") String clusterName,//集群
  5. @PathVariable("namespaceName") String namespaceName,// 主版本名称
  6. @RequestParam("releaseName") String releaseName, // 发布名称
  7. @RequestParam("branchName") String branchName,// 灰度名称 cluster
  8. @RequestParam(value = "deleteBranch", defaultValue = "true") boolean deleteBranch,// 是否删除灰度
  9. @RequestParam(name = "releaseComment", required = false) String releaseComment,// 评论
  10. @RequestParam(name = "isEmergencyPublish", defaultValue = "false") boolean isEmergencyPublish,// 是否紧急发布
  11. @RequestBody ItemChangeSets changeSets) {// 这个是 portal 发来的
  12. Namespace namespace = namespaceService.findOne(appId, clusterName, namespaceName);// 找到分支
  13. if (namespace == null) {
  14. throw new NotFoundException(String.format("Could not find namespace for %s %s %s", appId,
  15. clusterName, namespaceName));
  16. }
  17. // 合并改变 并且发布
  18. Release release = releaseService.mergeBranchChangeSetsAndRelease(namespace, branchName, releaseName,
  19. releaseComment, isEmergencyPublish, changeSets);
  20. // 是否删除分支
  21. if (deleteBranch) {
  22. namespaceBranchService.deleteBranch(appId, clusterName, namespaceName, branchName,
  23. NamespaceBranchStatus.MERGED, changeSets.getDataChangeLastModifiedBy());
  24. }
  25. // 保存发布消息到数据库
  26. messageSender.sendMessage(ReleaseMessageKeyGenerator.generate(appId, clusterName, namespaceName),
  27. Topics.APOLLO_RELEASE_TOPIC);
  28. return BeanUtils.transfrom(ReleaseDTO.class, release);
  29. }

这个接口接受 portal 调用,比较有趣的点是,这里的 changeSet 是 portal 计算的,而不是 admin 自己计算的。

然后,controller 层比较简单,数据校验,调用 Service,发送消息。

当然主要看看 Service。

主要是 releaseService 的 mergeBranchChangeSetsAndRelease 方法,看名字,任务很多:合并分支修改集合,并且发布。

代码如下:

  1. @Transactional
  2. public Release mergeBranchChangeSetsAndRelease(Namespace namespace, String branchName, String releaseName,
  3. String releaseComment, boolean isEmergencyPublish,
  4. ItemChangeSets changeSets) {
  5. // 检查锁
  6. checkLock(namespace, isEmergencyPublish, changeSets.getDataChangeLastModifiedBy());
  7. /// 更新 item
  8. itemSetService.updateSet(namespace, changeSets);
  9. // 找到最新发布的 release
  10. Release branchRelease = findLatestActiveRelease(namespace.getAppId(), branchName, namespace
  11. .getNamespaceName());
  12. // release Id
  13. long branchReleaseId = branchRelease == null ? 0 : branchRelease.getId();
  14. // 找到当前 namespace 的所有 Item(刚刚更新的)
  15. Map<String, String> operateNamespaceItems = getNamespaceItems(namespace);
  16. Map<String, Object> operationContext = Maps.newHashMap();
  17. // 构造操作上下文 sourceBranch=灰度名称 baseReleaseId=最新的releaseId isEmergencyPublish=是否紧急发布, 用于构建发布历史
  18. operationContext.put(ReleaseOperationContext.SOURCE_BRANCH, branchName);
  19. operationContext.put(ReleaseOperationContext.BASE_RELEASE_ID, branchReleaseId);
  20. operationContext.put(ReleaseOperationContext.IS_EMERGENCY_PUBLISH, isEmergencyPublish);
  21. // ReleaseHistory Audit 主版本
  22. return masterRelease(namespace, releaseName, releaseComment, operateNamespaceItems,
  23. changeSets.getDataChangeLastModifiedBy(),
  24. // 灰度合并回主分支发布
  25. ReleaseOperation.GRAY_RELEASE_MERGE_TO_MASTER, operationContext);
  26. }

代码很简单,步骤:

  1. 检查锁,和普通发布一样,判断修改者和发布者是不是同一个人。
  2. 根据 Portal 传递来的 changeSets 更新 item。
  3. 找到最新发布的 release(构建发布历史的上下文)。
  4. 发布主版本。

其中,updateSet 方法比较重要,要看看他是怎么更新 item 的。

方法很长,总之,就是将 changeSet 的内容保存到主版本的 namespace 下。

  1. @Transactional
  2. public ItemChangeSets updateSet(String appId, String clusterName,
  3. String namespaceName, ItemChangeSets changeSet) {
  4. // 最后改变数据的人
  5. String operator = changeSet.getDataChangeLastModifiedBy();
  6. // 改变数据的详细信息
  7. ConfigChangeContentBuilder configChangeContentBuilder = new ConfigChangeContentBuilder();
  8. // 如果创建了新的
  9. if (!CollectionUtils.isEmpty(changeSet.getCreateItems())) {
  10. // 循环
  11. for (ItemDTO item : changeSet.getCreateItems()) {
  12. // 转换
  13. Item entity = BeanUtils.transfrom(Item.class, item);
  14. entity.setDataChangeCreatedBy(operator);
  15. entity.setDataChangeLastModifiedBy(operator);
  16. // 保存 item 到数据库
  17. Item createdItem = itemService.save(entity);
  18. // 保存到 builder createItems List 中
  19. configChangeContentBuilder.createItem(createdItem);
  20. }
  21. // 最后记录审核
  22. auditService.audit("ItemSet", null, Audit.OP.INSERT, operator);
  23. }
  24. // 如果有修改的数据
  25. if (!CollectionUtils.isEmpty(changeSet.getUpdateItems())) {
  26. for (ItemDTO item : changeSet.getUpdateItems()) {
  27. // 转换并寻找
  28. Item entity = BeanUtils.transfrom(Item.class, item);
  29. Item managedItem = itemService.findOne(entity.getId());
  30. // 不存在抛出异常
  31. if (managedItem == null) {
  32. throw new NotFoundException(String.format("item not found.(key=%s)", entity.getKey()));
  33. }
  34. // 之前的数据
  35. Item beforeUpdateItem = BeanUtils.transfrom(Item.class, managedItem);
  36. //protect. only value,comment,lastModifiedBy,lineNum can be modified
  37. // 将之前数据内容更新
  38. managedItem.setValue(entity.getValue());
  39. managedItem.setComment(entity.getComment());
  40. managedItem.setLineNum(entity.getLineNum());
  41. managedItem.setDataChangeLastModifiedBy(operator);
  42. // 更新
  43. Item updatedItem = itemService.update(managedItem);
  44. // 更新 builder 中 value
  45. configChangeContentBuilder.updateItem(beforeUpdateItem, updatedItem);
  46. }
  47. // 最后审核 itemSet
  48. auditService.audit("ItemSet", null, Audit.OP.UPDATE, operator);
  49. }
  50. // 如果有删除的
  51. if (!CollectionUtils.isEmpty(changeSet.getDeleteItems())) {
  52. for (ItemDTO item : changeSet.getDeleteItems()) {
  53. // 数据库删除
  54. Item deletedItem = itemService.delete(item.getId(), operator);
  55. // 添加到 builder 中
  56. configChangeContentBuilder.deleteItem(deletedItem);
  57. }
  58. // 审核
  59. auditService.audit("ItemSet", null, Audit.OP.DELETE, operator);
  60. }
  61. // 如果 builder 中有内容
  62. if (configChangeContentBuilder.hasContent()){
  63. // 创建提交记录
  64. createCommit(appId, clusterName, namespaceName,
  65. configChangeContentBuilder.build(), // 将 build 变成 json 保存
  66. changeSet.getDataChangeLastModifiedBy());
  67. }
  68. return changeSet;
  69. }

在成功更新 itme 之后,便可以进行最终的发布了,发布很简单,就不展开讲了。

然后看看删除灰度,默认是要删除的。

步骤:

  1. 找到灰度发布的最新 release。
  2. 更新灰度规则,置空灰度规则。
  3. 删除灰度 cluster 和关联的 namespace。置于灰度为什么和 cluster 关联,而不是和 namespace 关联,这是因为最初的 apollo 没有设计灰度,后面加上灰度的时候,为了避免 namespace 大幅修改,就在 cluster 里加入父子逻辑了(咨询过作者)。
  4. 记录发布历史。根据是否 merge 记录是放弃灰度还是合并后删除,方便审计。

发布操作有很多类型,apollo 的常量如下:

  1. public interface ReleaseOperation {
  2. int NORMAL_RELEASE = 0;//普通发布
  3. int ROLLBACK = 1;// 回滚
  4. int GRAY_RELEASE = 2;// 灰度发布
  5. int APPLY_GRAY_RULES = 3;// 灰度规则更新
  6. int GRAY_RELEASE_MERGE_TO_MASTER = 4;// 灰度合并回主分支发布
  7. int MASTER_NORMAL_RELEASE_MERGE_TO_GRAY = 5;// 主分支发布灰度自动发布
  8. int MATER_ROLLBACK_MERGE_TO_GRAY = 6;// 主分支回滚灰度自动发布
  9. int ABANDON_GRAY_RELEASE = 7;//放弃灰度
  10. int GRAY_RELEASE_DELETED_AFTER_MERGE = 8;// 灰度版本合并后删除
  11. }

总结一下 admin 的发布流程:

1240

4. 总结

将 portal 和 admin 组合起来看,下图:

1240

转载于:https://www.cnblogs.com/stateis0/p/9479314.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/733433
推荐阅读
相关标签
  

闽ICP备14008679号