当前位置: 首页 > 工具软件 > Media Portal > 使用案例 >

Apollo源码-Portal发布配置

尹雅健
2023-12-01

前言

在更新Spring Cloud Alibaba Nacos时,想到之前阅读过Apollo的源码,便在这插入记录了过来,后续更新Nacos Config源码

Apollo简介

fork 源码地址 apollo源码
参考apollo架构中心设计
主要分为 Config ServiceAdmin ServicePortalClient 四部分
本文介绍的Portal 主要是提供用户管理界面,接口来自Admin Service提供

port页面触发配置新增

页面路由/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/item
后端调用ItemController#createItem :

  • 根据 appId、env、clusterName、namespaceName 远程调用 adminservice 获取namespace,并校验是否存在
  • 远程调用 adminservice 进行item的保存

adminservice 执行路由 /apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items 调用 ItemController#create 方法进行保存:

  • 根据appId、clusterName、namespaceName、key(portal新增页面的key)查询item是否存在,存在则不操作
  • 执行保存

portal页面触发配置修改

路由 /apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/items 此时调用 ItemController#modifyItemsByText 该方法内部会使用 RetryableRestTemplate

  • 调用admin服务的接口/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items 根据 appId、clusterName、namespaceName 获取所有的item
  • 获取 ConfigTextResolver 类型,例如前端传递 format = properties 则获取 PropertyResolver 实例并调用resolve方法
  • resolve方法主要处理,先将前端传来的configTest字符串根据\n空格切割
  • 继而遍历每一个item,再截取=号左边和后边放入到数组中
  • 紧接着和老数据进行比较,如果不存在key,则进行新增,存在且有修改,则进行修改操作 如果有需求,提交的数据与老的数据相比,没有则新增,有则修改,可以参考该段代码 PropertyResolver#handleNormalLine

小结 : 看完配置的新增和修改,我们知道portal提供了配置管理界面,并调用 adminservice 获取、修改配置数据。从apollo 提供的wiki文档来看,我们还没跟踪到与 configservice 的交互,它是如何拉取配置并实时更新通知客户端的?我们每新增一条配置,就往数据库插入一条item记录,那 adminservice 是如何管理这庞大的数据量的?带着这两个问题我们来看配置的发布功能。

portal页面触发配置发布

路由 /apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/releases 此时调用 ReleaseController#createRelease 由请求参数可知,发布的是app某个集群的namespace下的所有Item配置项。

  • 调用releaseService#publish进行发布
public ReleaseDTO createRelease(String appId, Env env, String clusterName, String namespace,
String releaseName, String releaseComment, String operator,
boolean isEmergencyPublish) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType(MediaType.APPLICATION_FORM_URLENCODED_VALUE + ";charset=UTF-8"));
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
parameters.add("name", releaseName);
parameters.add("comment", releaseComment);
parameters.add("operator", operator);
parameters.add("isEmergencyPublish", String.valueOf(isEmergencyPublish));
HttpEntity<MultiValueMap<String, String>> entity =
 new HttpEntity<>(parameters, headers);
ReleaseDTO response = restTemplate.post(env,      "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases",   entity,
ReleaseDTO.class, appId, clusterName, namespace);
return response;
}

使用 RetryableRestTemplate 这个类调用adminservice接口,根据请求路由,定位到 ReleaseController#publish
保存Release对象

Namespace namespace = namespaceService.findOne(appId, clusterName, namespaceName);
if (namespace == null) {
throw new NotFoundException(String.format("Could not find namespace for %s %s %s", appId,clusterName, namespaceName));
}
Release release = releaseService.publish(namespace, releaseName, releaseComment, operator, isEmergencyPublish);

首行代码也验证之前说的,发布的是namespace下所有的配置项。进入到publish方法,只看下面几行代码,其余行是关于灰度发布的

//master release
Map<String, Object> operationContext = Maps.newHashMap();
operationContext.put(ReleaseOperationContext.IS_EMERGENCY_PUBLISH, isEmergencyPublish);

Release release = masterRelease(namespace, releaseName, releaseComment, operateNamespaceItems,operator, ReleaseOperation.NORMAL_RELEASE,operationContext);

masterRelease方法参数operateNamespaceItems是以配置项item的key为key,value为value的map,进入masterRelease` 方法

private Release masterRelease(Namespace namespace, String releaseName, String releaseComment,
                         Map<String, String> configurations, String operator,
                         int releaseOperation, Map<String, Object> operationContext) {
//先查询出最新一条发布对象
Release lastActiveRelease = findLatestActiveRelease(namespace);
//如果没有发布过配置,则先前的releaseId为0,如果有,则取最新一条id
long previousReleaseId = lastActiveRelease == null ? 0 : lastActiveRelease.getId();
//保存发布对象到数据release.setConfigurations(gson.toJson(configurations));
Release release = createRelease(namespace, releaseName, releaseComment,
                             configurations, operator);

//保存发布历史对象
releaseHistoryService.createReleaseHistory(namespace.getAppId(),
                                      namespace.getClusterName(),
                                      namespace.getNamespaceName(),                                            namespace.getClusterName(),
                                      release.getId(),                                                        previousReleaseId,                                                      releaseOperation,
                                      operationContext,                                                        operator);
return release;
}

没什么特别的,新增发布对象数据、新增发布历史对象数据。createRelease 方法内 release.setConfigurations(gson.toJson(configurations)); 是将前面查询出来的namespace下的所有的配置项map即上文所说的 operateNamespaceItems , 使用gson转成json存储到Release对象的configurations字段中,createRelease 方法部分代码

release.setConfigurations(gson.toJson(configurations));
release = releaseRepository.save(release);
//删除namespace
namespaceLockService.unlock(namespace.getId());
//记录审计日志
auditService.audit(Release.class.getSimpleName(), release.getId(), Audit.OP.INSERT,release.getDataChangeCreatedBy());

小结 : 看到这里,从portal发布配置到调用adminservice新增Release发布对象,无非就是将appId下的某个集群下的某个命名空间的所有配置,转成key-value的map,set进Release对象的configurations字段,对于同一appId、clusterName、namespaces每一次点击发布,都会新增一条Release记录,那历史Release记录还需要维护?是否只需要保留最新的一条Release就可以了?带着疑问,我们来看 ReleaseController#publish 做的第二件事情

 类似资料: