}

hasContent

#hasContent() 方法,判断是否有变化。当且仅当有变化才记录 Commit。代码如下:

public boolean hasContent() {

return !createItems.isEmpty() || !updateItems.isEmpty() || !deleteItems.isEmpty();

}

build

#build() 方法,构建 Item 变化的 JSON 字符串。代码如下:

public String build() {

// 因为事务第一段提交并没有更新时间,所以build时统一更新

Date now = new Date();

for (Item item : createItems) {

item.setDataChangeLastModifiedTime(now);

}

for (ItemPair item : updateItems) {

item.newItem.setDataChangeLastModifiedTime(now);

}

for (Item item : deleteItems) {

item.setDataChangeLastModifiedTime(now);

}

// JSON 格式化成字符串

return gson.toJson(this);

}

  • 例子如下:

// 已经使用 http://tool.oschina.net/codeformat/json/ 进行格式化,实际是**紧凑型**

{

“createItems”: [ ],

“updateItems”: [

{

“oldItem”: {

“namespaceId”: 32,

“key”: “key4”,

“value”: “value4123”,

“comment”: “123”,

“lineNum”: 4,

“id”: 15,

“isDeleted”: false,

“dataChangeCreatedBy”: “apollo”,

“dataChangeCreatedTime”: “2018-04-27 16:49:59”,

“dataChangeLastModifiedBy”: “apollo”,

“dataChangeLastModifiedTime”: “2018-04-27 22:37:52”

},

“newItem”: {

“namespaceId”: 32,

“key”: “key4”,

“value”: “value41234”,

“comment”: “123”,

“lineNum”: 4,

“id”: 15,

“isDeleted”: false,

“dataChangeCreatedBy”: “apollo”,

“dataChangeCreatedTime”: “2018-04-27 16:49:59”,

“dataChangeLastModifiedBy”: “apollo”,

“dataChangeLastModifiedTime”: “2018-04-27 22:38:58”

}

}

],

“deleteItems”: [ ]

}

3. Portal 侧

================

3.1 ItemController

在 apollo-portal 项目中,

com.ctrip.framework.apollo.portal.controller.ItemController,提供 Item 的 API 。

在【添加配置项】的界面中,点击【提交】按钮,调用创建 Item 的 API 。

微服务配置中心 Apollo 源码解析——Portal 创建 Item

添加配置项

#createItem(appId, env, clusterName, namespaceName, ItemDTO) 方法,创建 Item 对象。代码如下:

1: @RestController

2: public class ItemController {

3:

4: @Autowired

5: private ItemService configService;

6: @Autowired

7: private UserInfoHolder userInfoHolder;

8:

9: @PreAuthorize(value = “@permissionValidator.hasModifyNamespacePermission(#appId, #namespaceName)”)

10: @RequestMapping(value = “/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/item”, method = RequestMethod.POST)

11: public ItemDTO createItem(@PathVariable String appId, @PathVariable String env,

12: @PathVariable String clusterName, @PathVariable String namespaceName,

13: @RequestBody ItemDTO item) {

14: // 校验 Item 格式正确

15: checkModel(isValidItem(item));

16: // protect

17: item.setLineNum(0);

18: item.setId(0);

19: // 设置 ItemDTO 的创建和修改人为当前管理员

20: String userId = userInfoHolder.getUser().getUserId();

21: item.setDataChangeCreatedBy(userId);

22: item.setDataChangeLastModifiedBy(userId);

23: // protect

24: item.setDataChangeCreatedTime(null);

25: item.setDataChangeLastModifiedTime(null);

26: // 保存 Item 到 Admin Service

27: return configService.createItem(appId, Env.valueOf(env), clusterName, namespaceName, item);

28: }

29:

30: // … 省略 deleteCluster 接口

31: }

  • POST `/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/item` 接口,Request Body 传递 JSON 对象。

  • @PreAuthorize(…) 注解,调用 PermissionValidator#hasModifyNamespacePermission(appId, namespaceName) 方法,校验是否有修改 Namespace 的权限。后续文章,详细分享。

  • com.ctrip.framework.apollo.common.dto.ItemDTO ,Item DTO 。代码如下:

public class ItemDTO extends BaseDTO {

/**

* Item 编号

*/

private long id;

/**

* Namespace 编号

*/

private long namespaceId;

/**

* 键

*/

private String key;

/**

* 值

*/

private String value;

/**

* 备注

*/

private String comment;

/**

* 行数

*/

private int lineNum;

}

  • 第 14 行:调用 #isValidItem(ItemDTO) 方法,校验 Item 格式正确。代码如下:

private boolean isValidItem(ItemDTO item) {

return Objects.nonNull(item) // 非空

&& !StringUtils.isContainEmpty(item.getKey()); // 键非空

}

  • 第 16 至 18 行 && 第 23 至 25 行:防御性编程,这几个参数不需要从 Portal 传递。

  • 第 19 至 22 行:设置 ItemDTO 的创建和修改人为当前管理员。

  • 第 27 行:调用 ConfigService#createItem(appId, Env, clusterName, namespaceName, ItemDTO) 方法,保存 Item 到 Admin Service 中。

3.2 ItemService

在 apollo-portal 项目中,

com.ctrip.framework.apollo.portal.service.ItemService ,提供 Item 的 Service 逻辑。

#createItem(appId, env, clusterName, namespaceName, ItemDTO) 方法,创建并保存 Item 到 Admin Service 。代码如下:

1: @Autowired

2: private AdminServiceAPI.NamespaceAPI namespaceAPI;

3: @Autowired

4: private AdminServiceAPI.ItemAPI itemAPI;

5:

6: public ItemDTO createItem(String appId, Env env, String clusterName, String namespaceName, ItemDTO item) {

7: // 校验 NamespaceDTO 是否存在。若不存在,抛出 BadRequestException 异常

8: NamespaceDTO namespace = namespaceAPI.loadNamespace(appId, env, clusterName, namespaceName);

9: if (namespace == null) {

10: throw new BadRequestException(“namespace:” + namespaceName + " not exist in env:" + env + “, cluster:” + clusterName);

11: }

12: // 设置 ItemDTO 的 `namespaceId`

13: item.setNamespaceId(namespace.getId());

14: // 保存 Item 到 Admin Service

15: ItemDTO itemDTO = itemAPI.createItem(appId, env, clusterName, namespaceName, item);

16: // 【TODO 6001】Tracer 日志

17: Tracer.logEvent(TracerEventType.MODIFY_NAMESPACE, String.format(“%s+%s+%s+%s”, appId, env, clusterName, namespaceName));

18: return itemDTO;

19: }

  • 第 7 至11 行:调用 NamespaceAPI#loadNamespace(appId, Env, clusterName, namespaceName) 方法,校验 Namespace 是否存在。若不存在,抛出 BadRequestException 异常。注意,此处是远程调用 Admin Service 的 API 。

  • 第 12 行:设置 ItemDTO 的 namespaceId 。

  • 第 15 行:调用 NamespaceAPI#createItem(appId, Env, clusterName, namespaceName, ItemDTO) 方法,保存 Item 到 Admin Service 。

  • 第 17 行:【TODO 6001】Tracer 日志

3.3 ItemAPI

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

微服务配置中心 Apollo 源码解析——Portal 创建 Item

ItemAPI

4. Admin Service 侧

=======================

4.1 ItemController

在 apollo-adminservice 项目中,

com.ctrip.framework.apollo.adminservice.controller.ItemController ,提供 Item 的 API 。

#create(appId, clusterName, namespaceName, ItemDTO) 方法,创建 Item ,并记录 Commit 。代码如下:

1: @RestController

2: public class ItemController {

3:

4: @Autowired

5: private ItemService itemService;

6: @Autowired

7: private CommitService commitService;

8:

9: @PreAcquireNamespaceLock

10: @RequestMapping(path = “/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items”, method = RequestMethod.POST)

11: public ItemDTO create(@PathVariable(“appId”) String appId,

12: @PathVariable(“clusterName”) String clusterName,

13: @PathVariable(“namespaceName”) String namespaceName,

14: @RequestBody ItemDTO dto) {

15: // 将 ItemDTO 转换成 Item 对象

16: Item entity = BeanUtils.transfrom(Item.class, dto);

17: // 创建 ConfigChangeContentBuilder 对象

18: ConfigChangeContentBuilder builder = new ConfigChangeContentBuilder();

19: // 校验对应的 Item 是否已经存在。若是,抛出 BadRequestException 异常。

20: Item managedEntity = itemService.findOne(appId, clusterName, namespaceName, entity.getKey());

21: if (managedEntity != null) {

22: throw new BadRequestException(“item already exist”);

23: } else {

24: // 保存 Item 对象

25: entity = itemService.save(entity);

26: // 添加到 ConfigChangeContentBuilder 中

27: builder.createItem(entity);

28: }

29: // 将 Item 转换成 ItemDTO 对象

30: dto = BeanUtils.transfrom(ItemDTO.class, entity);

31: // 创建 Commit 对象

32: Commit commit = new Commit();

33: commit.setAppId(appId);

34: commit.setClusterName(clusterName);

35: commit.setNamespaceName(namespaceName);

36: commit.setChangeSets(builder.build()); // ConfigChangeContentBuilder 构造变更

37: commit.setDataChangeCreatedBy(dto.getDataChangeLastModifiedBy());

38: commit.setDataChangeLastModifiedBy(dto.getDataChangeLastModifiedBy());

39: // 保存 Commit 对象

40: commitService.save(commit);

41: return dto;

42: }

43:

44: // … 省略其他接口和属性

45: }

  • POST `/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items` 接口,Request Body 传递 JSON 对象。

  • 第 16 行:调用 BeanUtils#transfrom(Class clazz, Object src) 方法,将 ItemDTO 转换成 Item 对象。

  • 第 18 行:创建 ConfigChangeContentBuilder 对象。

  • 第 19 至 22 行:调用 ItemService#findOne(appId, clusterName, namespaceName, key) 方法,校验对应的 Item 是否已经存在。若是,抛出 BadRequestException 异常。

  • 第 25 行:调用 ItemService#save(Item) 方法,保存 Item 对象。

  • 第 27 行:调用 ConfigChangeContentBuilder#createItem(Item) 方法,添加到 ConfigChangeContentBuilder 中。

  • 第 30 行:调用 BeanUtils#transfrom(Class clazz, Object src) 方法,将 Item 转换成 ItemDTO 对象。

  • 第 31 至 38 行:创建 Commit 对象。

  • 第 40 行:调用 CommitService#save(Commit) 方法,保存 Commit 对象。

4.2 ItemService

在 apollo-biz 项目中,

com.ctrip.framework.apollo.biz.service.ItemService ,提供 Item 的 Service 逻辑给 Admin Service 和 Config Service 。

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

1: @Autowired

2: private ItemRepository itemRepository;

3: @Autowired

4: private AuditService auditService;

5:

6: @Transactional

7: public Item save(Item entity) {

8: // 校验 Key 长度

9: checkItemKeyLength(entity.getKey());

10: // 校验 Value 长度

11: checkItemValueLength(entity.getNamespaceId(), entity.getValue());

12: // protection

13: entity.setId(0);

14: // 设置 Item 的行号,以 Namespace 下的 Item 最大行号 + 1 。

15: if (entity.getLineNum() == 0) {

16: Item lastItem = findLastOne(entity.getNamespaceId());

17: int lineNum = lastItem == null ? 1 : lastItem.getLineNum() + 1;

18: entity.setLineNum(lineNum);

19: }

20: // 保存 Item

21: Item item = itemRepository.save(entity);

22: // 记录 Audit 到数据库中

23: auditService.audit(Item.class.getSimpleName(), item.getId(), Audit.OP.INSERT, item.getDataChangeCreatedBy());

24: return item;

25: }

  • 第 9 行:调用 #checkItemKeyLength(key) 方法,校验 Key 长度。

  • 可配置 “item.value.length.limit” 在 ServerConfig 配置最大长度。

  • 默认最大长度为 128 。

  • 第 11 行:调用 #checkItemValueLength(namespaceId, value) 方法,校验 Value 长度。

  • 全局可配置 “item.value.length.limit” 在 ServerConfig 配置最大长度。

  • 自定义配置 “namespace.value.length.limit.override” 在 ServerConfig 配置最大长度。

  • 默认最大长度为 20000 。

  • 第 14 至 19 行:设置 Item 的行号,以 Namespace 下的 Item 最大行号 + 1 。#findLastOne(namespaceId) 方法,获得最大行号的 Item 对象,代码如下:

public Item findLastOne(long namespaceId) {

return itemRepository.findFirst1ByNamespaceIdOrderByLineNumDesc(namespaceId);

}

  • 第 21 行:调用 ItemRepository#save(Item) 方法,保存 Item 。

  • 第 23 行:记录 Audit 到数据库中

4.3 ItemRepository

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

public interface ItemRepository extends PagingAndSortingRepository<Item, Long> {

Item findByNamespaceIdAndKey(Long namespaceId, String key);

List findByNamespaceIdOrderByLineNumAsc(Long namespaceId);

List findByNamespaceId(Long namespaceId);

List findByNamespaceIdAndDataChangeLastModifiedTimeGreaterThan(Long namespaceId, Date date);

Item findFirst1ByNamespaceIdOrderByLineNumDesc(Long namespaceId);

@Modifying

@Query(“update Item set isdeleted=1,DataChange_LastModifiedBy = ?2 where namespaceId = ?1”)

int deleteByNamespaceId(long namespaceId, String operator);

}

4.4 CommitService

在 apollo-biz 项目中,

com.ctrip.framework.apollo.biz.service.CommitService ,提供 Commit 的 Service 逻辑给 Admin Service 和 Config Service 。

#save(Commit) 方法,保存 Item 对象 。代码如下:

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

总结

总体来说,如果你想转行从事程序员的工作,Java开发一定可以作为你的第一选择。但是不管你选择什么编程语言,提升自己的硬件实力才是拿高薪的唯一手段。

如果你以这份学习路线来学习,你会有一个比较系统化的知识网络,也不至于把知识学习得很零散。我个人是完全不建议刚开始就看《Java编程思想》、《Java核心技术》这些书籍,看完你肯定会放弃学习。建议可以看一些视频来学习,当自己能上手再买这些书看又是非常有收获的事了。


《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
(img-xBqKLQn1-1712921837785)]

[外链图片转存中…(img-wBYqip0q-1712921837786)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

总结

总体来说,如果你想转行从事程序员的工作,Java开发一定可以作为你的第一选择。但是不管你选择什么编程语言,提升自己的硬件实力才是拿高薪的唯一手段。

如果你以这份学习路线来学习,你会有一个比较系统化的知识网络,也不至于把知识学习得很零散。我个人是完全不建议刚开始就看《Java编程思想》、《Java核心技术》这些书籍,看完你肯定会放弃学习。建议可以看一些视频来学习,当自己能上手再买这些书看又是非常有收获的事了。

[外链图片转存中…(img-fmjTyfP4-1712921837786)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

Logo

一起探索未来云端世界的核心,云原生技术专区带您领略创新、高效和可扩展的云计算解决方案,引领您在数字化时代的成功之路。

更多推荐