Apollo 10 — adminService 全量发布
目錄
1. UI 界面
2. Portal 服務(wù)
當(dāng)我們點(diǎn)擊上面的發(fā)布按鈕的時(shí)候,調(diào)用的當(dāng)然是 portal 的接口。具體代碼如下:
/*** 全量發(fā)布* @param appId SampleApp* @param env DEV* @param clusterName default* @param namespaceName application* @param branchName 分支/灰度名稱* @param deleteBranch true* @param model {"releaseTitle":"20180716220550-gray-release-merge-to-master","releaseComment":"","isEmergencyPublish":false}* @return*/@PreAuthorize(value = "@permissionValidator.hasReleaseNamespacePermission(#appId, #namespaceName)")@RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}/merge", method = RequestMethod.POST)public ReleaseDTO merge(@PathVariable String appId, @PathVariable String env,@PathVariable String clusterName, @PathVariable String namespaceName,@PathVariable String branchName, @RequestParam(value = "deleteBranch", defaultValue = "true") boolean deleteBranch,@RequestBody NamespaceReleaseModel model) {// 如果是緊急發(fā)布,但該環(huán)境不允許緊急發(fā)布,拋出異常if (model.isEmergencyPublish() && !portalConfig.isEmergencyPublishAllowed(Env.fromString(env))) {throw new BadRequestException(String.format("Env: %s is not supported emergency publish now", env));}// 合并主版本和灰度版本, 得到一個(gè)發(fā)布 dtoReleaseDTO createdRelease = namespaceBranchService.merge(appId, Env.valueOf(env), clusterName, namespaceName, branchName,model.getReleaseTitle(), model.getReleaseComment(),model.isEmergencyPublish(), deleteBranch);ConfigPublishEvent event = ConfigPublishEvent.instance();event.withAppId(appId).withCluster(clusterName).withNamespace(namespaceName).withReleaseId(createdRelease.getId()).setMergeEvent(true).setEnv(Env.valueOf(env));publisher.publishEvent(event);// 發(fā)送郵件return createdRelease;}接口職責(zé)不多:是否符合緊急發(fā)布的數(shù)據(jù)校驗(yàn),調(diào)用 Service, 發(fā)布“配置發(fā)布”事件(發(fā)送郵件)。
看看調(diào)用 Service 的過程,該方法稱為 merge ,實(shí)際上就是合并灰度和主版本的配置。代碼如下:
public ReleaseDTO merge(String appId, Env env, String clusterName, String namespaceName,String branchName, String title, String comment,boolean isEmergencyPublish, boolean deleteBranch) {// 計(jì)算 changeSetsItemChangeSets changeSets = calculateBranchChangeSet(appId, env, clusterName, namespaceName, branchName);// 調(diào)用 admin 服務(wù)ReleaseDTO mergedResult =releaseService.updateAndPublish(appId, env, clusterName, namespaceName, title, comment,branchName, isEmergencyPublish, deleteBranch, changeSets);Tracer.logEvent(TracerEventType.MERGE_GRAY_RELEASE,String.format("%s+%s+%s+%s", appId, env, clusterName, namespaceName));return mergedResult;}做了 2 件事情: 計(jì)算 change 集合,調(diào)用 admin 服務(wù)。很明顯,計(jì)算 change 對(duì)于 protal 非常重要。
calculateBranchChangeSet 方法主要將灰度配置和主版本配置合并。
代碼:
private ItemChangeSets calculateBranchChangeSet(String appId, Env env, String clusterName, String namespaceName,String branchName) {NamespaceBO parentNamespace = namespaceService.loadNamespaceBO(appId, env, clusterName, namespaceName);// 父版本 namespaceif (parentNamespace == null) {throw new BadRequestException("base namespace not existed");}if (parentNamespace.getItemModifiedCnt() > 0) {throw new BadRequestException("Merge operation failed. Because master has modified items");}List<ItemDTO> masterItems = itemService.findItems(appId, env, clusterName, namespaceName);// 主版本 items List<ItemDTO> branchItems = itemService.findItems(appId, env, branchName, namespaceName);// 子版本 items ItemChangeSets changeSets = itemsComparator.compareIgnoreBlankAndCommentItem(parentNamespace.getBaseInfo().getId(),masterItems, branchItems);// 得到 changeSetchangeSets.setDeleteItems(Collections.emptyList());// 防止誤刪除,emm,灰度的內(nèi)容并不是全量的,因此上面的計(jì)算有些問題,并且目前沒有刪除功能。所以這里可以置空。changeSets.setDataChangeLastModifiedBy(userInfoHolder.getUser().getUserId());return changeSets; }步驟:
這里需要注意的是計(jì)算差異到底是怎么計(jì)算的,為什么后面有置空 deleteItem 的操作。
我就不貼全部的方法了,貼一下對(duì)刪除操作有影響的代碼:
/** 比較,忽略空格,返回一個(gè)改變的 items */ public ItemChangeSets compareIgnoreBlankAndCommentItem(long baseNamespaceId, List<ItemDTO> baseItems, List<ItemDTO> targetItems){// 忽略新增/修改 item 代碼......// 處理刪除,但這個(gè)邏輯似乎不對(duì). 不過此類不知道數(shù)據(jù)來源,工具類沒有問題.for (ItemDTO item: baseItems){// 主版本String key = item.getKey();ItemDTO targetItem = targetItemMap.get(key);if(targetItem == null){//delete// 如果灰度版本里沒有,說明刪除了.changeSets.addDeleteItem(item);// 添加進(jìn)刪除集合}}return changeSets; }可以看到,這段代碼里,循環(huán)主版本,逐個(gè)對(duì)比灰度版本,如果灰度版本里沒有,就添加進(jìn) delete 集合,而我們知道,灰度版本的 item 只有修改的和新增的,這時(shí),將導(dǎo)致誤刪除。
但這個(gè)工具類的計(jì)算是沒有問題的,有問題的是外層數(shù)據(jù)的完整性。
因此需要在外面打個(gè)補(bǔ)丁:changeSets.setDeleteItems(Collections.emptyList());
好,計(jì)算完 changeSet,就要調(diào)用 admin 服務(wù)了,并且把 changeSet 傳遞過去,然后返回一個(gè) release 對(duì)象,表示發(fā)布成功,并發(fā)布事件。
在分析 admin 之前,總結(jié)一下 protal 的流程:
3. admin 服務(wù)
從 portal 的代碼中,可以看到,調(diào)用的是 admin 的 updateAndPublish 方法接口,看看這個(gè)接口:
 位置 : com.ctrip.framework.apollo.adminservice.controller.ReleaseController.java
 代碼如下:
這個(gè)接口接受 portal 調(diào)用,比較有趣的點(diǎn)是,這里的 changeSet 是 portal 計(jì)算的,而不是 admin 自己計(jì)算的。
然后,controller 層比較簡(jiǎn)單,數(shù)據(jù)校驗(yàn),調(diào)用 Service,發(fā)送消息。
當(dāng)然主要看看 Service。
主要是 releaseService 的 mergeBranchChangeSetsAndRelease 方法,看名字,任務(wù)很多:合并分支修改集合,并且發(fā)布。
代碼如下:
@Transactional public Release mergeBranchChangeSetsAndRelease(Namespace namespace, String branchName, String releaseName,String releaseComment, boolean isEmergencyPublish,ItemChangeSets changeSets) {// 檢查鎖checkLock(namespace, isEmergencyPublish, changeSets.getDataChangeLastModifiedBy());/// 更新 itemitemSetService.updateSet(namespace, changeSets);// 找到最新發(fā)布的 releaseRelease branchRelease = findLatestActiveRelease(namespace.getAppId(), branchName, namespace.getNamespaceName());// release Idlong branchReleaseId = branchRelease == null ? 0 : branchRelease.getId();// 找到當(dāng)前 namespace 的所有 Item(剛剛更新的)Map<String, String> operateNamespaceItems = getNamespaceItems(namespace);Map<String, Object> operationContext = Maps.newHashMap();// 構(gòu)造操作上下文 sourceBranch=灰度名稱 baseReleaseId=最新的releaseId isEmergencyPublish=是否緊急發(fā)布, 用于構(gòu)建發(fā)布?xì)v史operationContext.put(ReleaseOperationContext.SOURCE_BRANCH, branchName);operationContext.put(ReleaseOperationContext.BASE_RELEASE_ID, branchReleaseId);operationContext.put(ReleaseOperationContext.IS_EMERGENCY_PUBLISH, isEmergencyPublish);// ReleaseHistory Audit 主版本return masterRelease(namespace, releaseName, releaseComment, operateNamespaceItems,changeSets.getDataChangeLastModifiedBy(),// 灰度合并回主分支發(fā)布ReleaseOperation.GRAY_RELEASE_MERGE_TO_MASTER, operationContext);}代碼很簡(jiǎn)單,步驟:
其中,updateSet 方法比較重要,要看看他是怎么更新 item 的。
方法很長(zhǎng),總之,就是將 changeSet 的內(nèi)容保存到主版本的 namespace 下。
@Transactional public ItemChangeSets updateSet(String appId, String clusterName,String namespaceName, ItemChangeSets changeSet) {// 最后改變數(shù)據(jù)的人String operator = changeSet.getDataChangeLastModifiedBy();// 改變數(shù)據(jù)的詳細(xì)信息ConfigChangeContentBuilder configChangeContentBuilder = new ConfigChangeContentBuilder();// 如果創(chuàng)建了新的if (!CollectionUtils.isEmpty(changeSet.getCreateItems())) {// 循環(huán)for (ItemDTO item : changeSet.getCreateItems()) {// 轉(zhuǎn)換Item entity = BeanUtils.transfrom(Item.class, item);entity.setDataChangeCreatedBy(operator);entity.setDataChangeLastModifiedBy(operator);// 保存 item 到數(shù)據(jù)庫Item createdItem = itemService.save(entity);// 保存到 builder createItems List 中configChangeContentBuilder.createItem(createdItem);}// 最后記錄審核auditService.audit("ItemSet", null, Audit.OP.INSERT, operator);}// 如果有修改的數(shù)據(jù)if (!CollectionUtils.isEmpty(changeSet.getUpdateItems())) {for (ItemDTO item : changeSet.getUpdateItems()) {// 轉(zhuǎn)換并尋找Item entity = BeanUtils.transfrom(Item.class, item);Item managedItem = itemService.findOne(entity.getId());// 不存在拋出異常if (managedItem == null) {throw new NotFoundException(String.format("item not found.(key=%s)", entity.getKey()));}// 之前的數(shù)據(jù)Item beforeUpdateItem = BeanUtils.transfrom(Item.class, managedItem);//protect. only value,comment,lastModifiedBy,lineNum can be modified// 將之前數(shù)據(jù)內(nèi)容更新managedItem.setValue(entity.getValue());managedItem.setComment(entity.getComment());managedItem.setLineNum(entity.getLineNum());managedItem.setDataChangeLastModifiedBy(operator);// 更新Item updatedItem = itemService.update(managedItem);// 更新 builder 中 valueconfigChangeContentBuilder.updateItem(beforeUpdateItem, updatedItem);}// 最后審核 itemSetauditService.audit("ItemSet", null, Audit.OP.UPDATE, operator);}// 如果有刪除的if (!CollectionUtils.isEmpty(changeSet.getDeleteItems())) {for (ItemDTO item : changeSet.getDeleteItems()) {// 數(shù)據(jù)庫刪除Item deletedItem = itemService.delete(item.getId(), operator);// 添加到 builder 中configChangeContentBuilder.deleteItem(deletedItem);}// 審核auditService.audit("ItemSet", null, Audit.OP.DELETE, operator);}// 如果 builder 中有內(nèi)容if (configChangeContentBuilder.hasContent()){// 創(chuàng)建提交記錄createCommit(appId, clusterName, namespaceName,configChangeContentBuilder.build(), // 將 build 變成 json 保存changeSet.getDataChangeLastModifiedBy());}return changeSet; }在成功更新 itme 之后,便可以進(jìn)行最終的發(fā)布了,發(fā)布很簡(jiǎn)單,就不展開講了。
然后看看刪除灰度,默認(rèn)是要?jiǎng)h除的。
步驟:
發(fā)布操作有很多類型,apollo 的常量如下:
public interface ReleaseOperation {int NORMAL_RELEASE = 0;//普通發(fā)布int ROLLBACK = 1;// 回滾int GRAY_RELEASE = 2;// 灰度發(fā)布int APPLY_GRAY_RULES = 3;// 灰度規(guī)則更新int GRAY_RELEASE_MERGE_TO_MASTER = 4;// 灰度合并回主分支發(fā)布int MASTER_NORMAL_RELEASE_MERGE_TO_GRAY = 5;// 主分支發(fā)布灰度自動(dòng)發(fā)布int MATER_ROLLBACK_MERGE_TO_GRAY = 6;// 主分支回滾灰度自動(dòng)發(fā)布int ABANDON_GRAY_RELEASE = 7;//放棄灰度int GRAY_RELEASE_DELETED_AFTER_MERGE = 8;// 灰度版本合并后刪除 }總結(jié)一下 admin 的發(fā)布流程:
4. 總結(jié)
將 portal 和 admin 組合起來看,下圖:
轉(zhuǎn)載于:https://www.cnblogs.com/stateis0/p/9479314.html
總結(jié)
以上是生活随笔為你收集整理的Apollo 10 — adminService 全量发布的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: Linux下安装scikit-learn
- 下一篇: [转载]Linux下getopt()函数
