您的位置 首页 > 腾讯云社区

Apollo 源码解析 —— Portal 创建 Cluster---芋道源码

摘要: 原创出处 http://www.iocoder.cn/Apollo/portal-create-cluster/ 「芋道源码」欢迎转载,保留摘要,谢谢!

1. 概述2. Cluster3. Portal 侧3.1 ClusterController3.2 ClusterService3.3 ClusterAPI4. Admin Service 侧4.1 ClusterController4.2 ClusterService4.3 ClusterRepository666. 彩蛋

阅读源码最好的方式,是使用 IDEA 进行调试 Apollo 源码,不然会一脸懵逼。 胖友可以点击「芋道源码」扫码关注,回复 git018 关键字 获得艿艿添加了中文注释的 Apollo 源码地址。 阅读源码很孤单,加入源码交流群,一起坚持!

1. 概述

老艿艿:本系列假定胖友已经阅读过 《Apollo 官方 wiki 文档》 。

本文分享 Portal 创建 Cluster 的流程,整个过程涉及 Portal、Admin Service ,如下图所示:

流程

下面,我们先来看看 Cluster 的实体结构

老艿艿:因为 Portal 是管理后台,所以从代码实现上,和业务系统非常相像。也因此,本文会略显啰嗦。

2. Cluster

com.ctrip.framework.apollo.biz.entity.Cluster ,继承 BaseEntity 抽象类,Cluster 实体。代码如下:

@Entity @Table(name = "Cluster") @SQLDelete(sql = "Update Cluster set isDeleted = 1 where id = ?") @Where(clause = "isDeleted = 0") public class Cluster extends BaseEntity implements Comparable<Cluster> { /** * 名字 */ @Column(name = "Name", nullable = false) private String name; /** * App 编号 {@link } */ @Column(name = "AppId", nullable = false) private String appId; /** * 父 App 编号 */ @Column(name = "ParentClusterId", nullable = false) private long parentClusterId; } appId 字段,App 编号,指向对应的 App 。App : Cluster = 1 : N 。parentClusterId 字段,父 App 编号。用于灰度发布,在 《Apollo 源码解析 —— Portal 创建灰度》 有详细解析。3. Portal 侧3.1 ClusterController

在 apollo-portal 项目中,com.ctrip.framework.apollo.portal.controller.ClusterController ,提供 Cluster 的 API 。

在创建 Cluster的界面中,点击【提交】按钮,调用创建 Cluster 的 API 。

创建 Cluster

代码如下:

1: @RestController 2: public class ClusterController { 3: 4: @Autowired 5: private ClusterService clusterService; 6: @Autowired 7: private UserInfoHolder userInfoHolder; 8: 9: @PreAuthorize(value = "@permissionValidator.hasCreateClusterPermission(#appId)") 10: @RequestMapping(value = "apps/{appId}/envs/{env}/clusters", method = RequestMethod.POST) 11: public ClusterDTO createCluster(@PathVariable String appId, @PathVariable String env, 12: @RequestBody ClusterDTO cluster) { 13: // 校验 ClusterDTO 非空 14: checkModel(Objects.nonNull(cluster)); 15: // 校验 ClusterDTO 的 `appId` 和 `name` 非空。 16: RequestPrecondition.checkArgumentsNotEmpty(cluster.getAppId(), cluster.getName()); 17: // 校验 ClusterDTO 的 `name` 格式正确。 18: if (!InputValidator.isValidClusterNamespace(cluster.getName())) { 19: throw new BadRequestException(String.format("Cluster格式错误: %s", InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE)); 20: } 21: // 设置 ClusterDTO 的创建和修改人为当前管理员 22: String operator = userInfoHolder.getUser().getUserId(); 23: cluster.setDataChangeLastModifiedBy(operator); 24: cluster.setDataChangeCreatedBy(operator); 25: // 创建 Cluster 到 Admin Service 26: return clusterService.createCluster(Env.valueOf(env), cluster); 27: } 28: 29: // ... 省略 deleteCluster 接口 30: } POST apps/{appId}/envs/{env}/cluster 接口,Request Body 传递 JSON 对象。@PreAuthorize(...) 注解,调用 PermissionValidator#hasCreateClusterPermission(appId,) 方法,校验是否有创建 Cluster 的权限。后续文章,详细分享。第 14 行:校验 ClusterDTO 非空。注意,此处使用的接收请求参数是 ClusterDTO 。第 16 行:调用 RequestPrecondition#checkArgumentsNotEmpty(String... args) 方法,校验 ClusterDTO 的 appId 和 name 非空。第 16 至 21 行:调用 InputValidator#isValidClusterNamespace(name) 方法,校验 ClusterDTO 的 name 格式正确,符合 [0-9a-zA-Z_.-]+" 格式。第 21 至 24 行:设置 ClusterDTO 的创建和修改人为当前管理员。第 26 行:调用 ClusterService#createCluster(Env, ClusterDTO) 方法,创建并保存 Cluster 到 Admin Service 。在 「3.2 ClusterService」 中,详细解析。3.2 ClusterService

在 apollo-portal 项目中,com.ctrip.framework.apollo.portal.service.ClusterService ,提供 Cluster 的 Service 逻辑。

#createCluster(Env, ClusterDTO) 方法,创建并保存 Cluster 到 Admin Service 。代码如下:

1: @Autowired 2: private AdminServiceAPI.ClusterAPI clusterAPI; 3: 4: public ClusterDTO createCluster(Env env, ClusterDTO cluster) { 5: // 根据 `appId` 和 `name` 校验 Cluster 的唯一性 6: if (!clusterAPI.isClusterUnique(cluster.getAppId(), env, cluster.getName())) { 7: throw new BadRequestException(String.format("cluster %s already exists.", cluster.getName())); 8: } 9: // 创建 Cluster 到 Admin Service 10: ClusterDTO clusterDTO = clusterAPI.create(env, cluster); 11: // 【TODO 6001】Tracer 日志 12: Tracer.logEvent(TracerEventType.CREATE_CLUSTER, cluster.getAppId(), "0", cluster.getName()); 13: return clusterDTO; 14: } 第 5 至 8 行:调用 ClusterAPI#isClusterUnique(appId, Env, clusterName) 方法,根据 appId 和 name 校验 Cluster 的唯一性。注意,此处是远程调用 Admin Service 的 API 。第 10 行:调用 ClusterAPI#create(Env, ClusterDTO) 方法,创建 Cluster 到 Admin Service 。第 12 行:【TODO 6001】Tracer 日志3.3 ClusterAPI

com.ctrip.framework.apollo.portal.api.ClusterAPI ,实现 API 抽象类,封装对 Admin Service 的 Cluster 模块的 API 调用。代码如下:

ClusterAPI

4. Admin Service 侧4.1 ClusterController

在 apollo-adminservice 项目中, com.ctrip.framework.apollo.adminservice.controller.ClusterController ,提供 Cluster 的 API 。

#create(appId, autoCreatePrivateNamespace, ClusterDTO) 方法,创建 Cluster 。代码如下:

@RestController 1: public class ClusterController { 2: 3: @Autowired 4: private ClusterService clusterService; 5: 6: @RequestMapping(path = "/apps/{appId}/clusters", method = RequestMethod.POST) 7: public ClusterDTO create(@PathVariable("appId") String appId, 8: @RequestParam(value = "autoCreatePrivateNamespace", defaultValue = "true") boolean autoCreatePrivateNamespace, 9: @RequestBody ClusterDTO dto) { 10: // 校验 ClusterDTO 的 `name` 格式正确。 11: if (!InputValidator.isValidClusterNamespace(dto.getName())) { 12: throw new BadRequestException(String.format("Cluster格式错误: %s", InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE)); 13: } 14: // 将 ClusterDTO 转换成 Cluster 对象 15: Cluster entity = BeanUtils.transfrom(Cluster.class, dto); 16: // 判断 `name` 在 App 下是否已经存在对应的 Cluster 对象。若已经存在,抛出 BadRequestException 异常。 17: Cluster managedEntity = clusterService.findOne(appId, entity.getName()); 18: if (managedEntity != null) { 19: throw new BadRequestException("cluster already exist."); 20: } 21: // 保存 Cluster 对象,并创建其 Namespace 22: if (autoCreatePrivateNamespace) { 23: entity = clusterService.saveWithInstanceOfAppNamespaces(entity); 24: // 保存 Cluster 对象,不创建其 Namespace 25: } else { 26: entity = clusterService.saveWithoutInstanceOfAppNamespaces(entity); 27: } 28: // 将保存的 Cluster 对象转换成 ClusterDTO 29: dto = BeanUtils.transfrom(ClusterDTO.class, entity); 30: return dto; 31: } 32: 33: // ... 省略其他接口和属性 34: } POST /apps/{appId}/clusters 接口,Request Body 传递 JSON 对象。第 10 至 13 行:调用 InputValidator#isValidClusterNamespace(name) 方法,校验 ClusterDTO 的 name 格式正确,符合 [0-9a-zA-Z_.-]+" 格式。第 15 行:调用 BeanUtils#transfrom(Class<T> clazz, Object src) 方法,将 ClusterDTO 转换成 Cluster 对象。第 16 至 20 行:调用 ClusterService#findOne(appId, name) 方法,校验 name 在 App 下,是否已经存在对应的 Cluster 对象。若已经存在,抛出 BadRequestException 异常。第 21 至 23 行:若 autoCreatePrivateNamespace = true 时,调用 ClusterService#saveWithInstanceOfAppNamespaces(Cluster) 方法,保存 Cluster 对象,并创建其 Namespace 。第 24 至 27 行:若 autoCreatePrivateNamespace = false 时,调用 ClusterService#saveWithoutInstanceOfAppNamespaces(Cluster) 方法,保存 Cluster 对象,不创建其 Namespace 。第 29 行:调用 BeanUtils#transfrom(Class<T> clazz, Object src) 方法,将 Cluster 转换成 ClusterDTO 对象。4.2 ClusterService

在 apollo-biz 项目中,com.ctrip.framework.apollo.biz.service.ClusterService ,提供 Cluster 的 Service 逻辑给 Admin Service 和 Config Service 。

#saveWithInstanceOfAppNamespaces(Cluster) 方法,保存 Cluster 对象,并创建其 Namespace 。代码如下:

1: @Autowired 2: private ClusterRepository clusterRepository; 3: @Autowired 4: private AuditService auditService; 5: @Autowired 6: private NamespaceService namespaceService; 7: 8: @Transactional 9: public Cluster saveWithInstanceOfAppNamespaces(Cluster entity) { 10: // 保存 Cluster 对象 11: Cluster savedCluster = saveWithoutInstanceOfAppNamespaces(entity); 12: // 创建 Cluster 的 Namespace 们 13: namespaceService.instanceOfAppNamespaces(savedCluster.getAppId(), savedCluster.getName(), savedCluster.getDataChangeCreatedBy()); 14: return savedCluster; 15: } 第 11 行:调用 #saveWithoutInstanceOfAppNamespaces(Cluster) 方法,保存 Cluster 对象。第 13 行:调用 NamespaceService#instanceOfAppNamespaces(appId, clusterName, createBy) 方法,创建 Cluster 的 Namespace 们。在 《Apollo 源码解析 —— Portal 创建 Namespace》 中,有详细解析。

#saveWithoutInstanceOfAppNamespaces(Cluster) 方法,保存 Cluster 对象。代码如下:

@Transactional public Cluster saveWithoutInstanceOfAppNamespaces(Cluster entity) { // 判断 `name` 在 App 下是否已经存在对应的 Cluster 对象。若已经存在,抛出 BadRequestException 异常。 if (!isClusterNameUnique(entity.getAppId(), entity.getName())) { throw new BadRequestException("cluster not unique"); } // 保存 Cluster 对象到数据库 entity.setId(0);//protection Cluster cluster = clusterRepository.save(entity); // 【TODO 6001】Tracer 日志 auditService.audit(Cluster.class.getSimpleName(), cluster.getId(), Audit.OP.INSERT, cluster.getDataChangeCreatedBy()); return cluster; } 4.3 ClusterRepository

com.ctrip.framework.apollo.biz.repository.ClusterRepository ,继承 org.springframework.data.repository.PagingAndSortingRepository 接口,提供 Cluster 的数据访问 给 Admin Service 和 Config Service 。代码如下:

public interface ClusterRepository extends PagingAndSortingRepository<Cluster, Long> { List<Cluster> findByAppIdAndParentClusterId(String appId, Long parentClusterId); List<Cluster> findByAppId(String appId); Cluster findByAppIdAndName(String appId, String name); List<Cluster> findByParentClusterId(Long parentClusterId); } 666. 彩蛋

? 有点水更,写的自己都不好意思了。

---来自腾讯云社区的---芋道源码

关于作者: 瞎采新闻

这里可以显示个人介绍!这里可以显示个人介绍!

热门文章

留言与评论(共有 0 条评论)
   
验证码: