在更新Spring Cloud Alibaba Nacos时,想到之前阅读过Apollo的源码,便在这插入记录了过来,后续更新Nacos Config源码
fork 源码地址 apollo源码
参考apollo架构中心设计
主要分为 Config Service、Admin Service、Portal、Client 四部分
本文介绍的Portal 主要是提供用户管理界面,接口来自Admin Service提供
页面路由/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是否存在,存在则不操作路由 /apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/items
此时调用 ItemController#modifyItemsByText
该方法内部会使用 RetryableRestTemplate
/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items
根据 appId、clusterName、namespaceName
获取所有的itemConfigTextResolver
类型,例如前端传递 format = properties
则获取 PropertyResolver
实例并调用resolve方法PropertyResolver#handleNormalLine
小结 : 看完配置的新增和修改,我们知道portal提供了配置管理界面,并调用 adminservice
获取、修改配置数据。从apollo
提供的wiki文档来看,我们还没跟踪到与 configservice
的交互,它是如何拉取配置并实时更新通知客户端的?我们每新增一条配置,就往数据库插入一条item记录,那 adminservice
是如何管理这庞大的数据量的?带着这两个问题我们来看配置的发布功能。
路由 /apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/releases
此时调用 ReleaseController#createRelease
由请求参数可知,发布的是app某个集群的namespace下的所有Item配置项。
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
做的第二件事情