基于SpringBoot的Tcc Java Client 增强组件,Tcc-plus的设计与实现

基于SpringBoot的Tcc Java Client 增强组件:Tcc-plus的设计与实现

背景

TCC 提供了非常灵活的配置管理,并具备丰富的功能,包括版本管理、权限管理、多环境支持、灰度发布等。

开发此工具的动机

以下几个场景促使我们开发了这个组件:

  • 原生侵入性:对代码有一定侵入,需要使用 @DynamicConfig 注解,与业务代码耦合。
  • 远程配置限制:对于大量基于 Spring Boot 的组件,无法直接使用 TCC 的远程配置能力。例如,需要修改请求控制参数、数据库连接参数等基础组件配置,或业务自身的其他 Spring Boot 配置变更场景。这些配置未在 TCC 上维护,需要修改 application.yml 等配置文件或启动参数/环境变量,前者发布流程较长,后者不便于集中维护且无法动态生效。
  • 安全性考虑:基于安全原因,一些组件的秘钥配置不建议放在工程文件中。可以利用 TCC 权限管理提高安全性,但同样不能直接配置在 TCC,需要引入额外代码处理。
  • 日志级别调整:调整 logger 日志级别的场景,同样不能直接使用 TCC 动态灵活变更配置。

基于以上原因,我们开发了 Tcc-plus,与原生 Spring Boot 结合更紧密。

tcc-plus简介

Tcc-plus 基于 Spring Boot 和 Spring Cloud Context,在启动阶段增强 TCC,兼容原生 TCC 功能,并具备以下特性:

  • 配置源集成:基于 TCC 的配置源作为 Spring PropertySource,启动时绑定 Spring Bean。
  • 配置变更监听:监听 TCC 配置变更,实现 Bean 重绑定。

主要特点

  • 无侵入性增强:仅对 TCC SDK 进行增强,不改变其原生功能,二者共存。
  • 业务代码简洁:业务使用 Spring Boot 原生方式即可使用 TCC 远程数据源,无需额外侵入。
  • 多格式支持:支持 propertiesyamljson 格式的配置解析。
  • 动态绑定:支持 @ConfigurationProperties@Value、Logger 日志级别等配置的动态绑定。
  • 版本兼容:兼容不同版本的 Spring Boot 环境。

接入指导

接入指导文档

设计与实现

核心流程

下图展示了原生 Spring、TCC 和 Tcc-plus 组件在 Spring Boot 启动过程中的扩展原理,不同颜色分别表示不同组件。

在这里插入图片描述

核心步骤
  1. Spring Cloud 上下文启动阶段

    • 读取 Tcc-plus 配置,加载自定义 Locator
  2. Spring Boot 上下文准备阶段

    • 自定义 Locator 创建远程属性源 TccPropertySource,通过原生 TccConfigClient 获取配置中心数据加载到 Environment
  3. Spring Boot 上下文刷新阶段

    • TccWatchAutoConfiguration 绑定监听处理器到 TccConfigClient
  4. TCC 配置变更时

    • 利用 ApplicationEventPublisher 发布 EnvironmentChangeEvent,触发重新绑定。

详细说明

1. prepareEnvironment:创建 Spring Cloud 上下文阶段
  • BootstrapApplicationListener 监听 ApplicationEnvironmentPreparedEvent,创建 Spring Cloud 上下文。
  • 读取 Spring Cloud 上下文引导配置,默认为 bootstrap.yml/properties
  • 创建 TccPlusProperties:保存从 bootstrap.yml 读取的 Tcc-plus 配置。
  • 创建 TccPropertySourceLocator:Spring Cloud 的 PropertySourceLocator 实现类,其 locate 方法在准备 Spring Boot 上下文阶段触发,并载入到 Environment,用于加载远程配置数据。
2. prepareContext:准备 Spring Boot 上下文阶段
  • Spring Cloud 的 PropertySourceBootstrapConfiguration 实现了 ApplicationContextInitializer,在此阶段触发其 initialize 方法,回调所有 Locatorlocate 方法。
  • 自定义的 TccPropertySourceLocator 创建 TccPropertySource,通过原生 TccConfigClient 拉取 TCC 配置中心数据。
  • 拉取的配置数据为 String 类型,根据 Tcc-plus 配置的 key 后缀使用 yamlLoaderpropertiesLoader 解析,解析后的配置载入到 EnvironmentPropertySources,并排序高于其他 Spring Boot 的 PropertySource,确保在 Spring IoC 创建 Bean 时,远程配置的优先级高于本地配置和启动参数等。
3. refreshContext:刷新 Spring Boot 上下文阶段
  • TccWatchAutoConfiguration 作为 Spring Boot 上下文,注入 ApplicationEventPublisherRefreshScope,绑定 handleChangeEventTccConfigClient 的配置变更处理器。
  • 容器创建其他 Bean(包括 ConfigurationProperties@ValueLoggingSystem 等)时,将优先使用 TccPropertySource 加载的配置参数。
4. handleChangeEvent:TCC 配置变更
  • TccConfigClient 默认每 10 秒读取 TCC 配置中心最新数据,检测到变更时通过异步线程触发 Listener 的 onLoad 方法。

  • TccWatchAutoConfiguration 中,将 handleChangeEvent 绑定到 onLoad 方法。

    变更处理步骤
    1. TccConfigClient 读取最新配置,解析为 Properties
    2. 对比新旧值差异。
    3. 封装 RichEnvironmentChangeEvent,发布事件;ConfigurationPropertiesRebinderLoggingRebinder 将监听该事件,分别触发 @ConfigurationProperties 的 Bean 重绑定和 LoggingSystem 的刷新。
    4. 刷新 RefreshScope,标注了 @RefreshScope 的 Bean 将被清空缓存,重新 getBean 将根据最新 Environment 创建 Bean。
ModuleConfigDuplicateChecker

Tcc-plus 禁止多个远程配置存在相同的 key(此 key 为 Spring 配置项的 key,非 TCC 的 key)。在启动阶段和配置变更阶段分别进行校验拦截。

知识点补充

整体实现方案需要了解一定的 Spring 原理和 TCC 本身的实现原理,以下是补充说明:

1. Spring 相关知识

Spring Boot 启动流程

Spring Boot 的启动过程复杂,提供了丰富的扩展点,允许用户在各个阶段运行自定义组件。以下是几个关键步骤:

  1. 创建 SpringApplication

    • spring.factories 提取 ApplicationListener,若项目依赖了 spring-cloud-contextBootstrapApplicationListener 将被注册到 SpringApplication
  2. prepareEnvironment

    • Environment 创建完成,发布 ApplicationEnvironmentPreparedEvent 事件,BootstrapApplicationListener 监听该事件,创建一个 Spring Cloud 上下文的 SpringApplication 并运行。
    • 这里将加载 bootstrap.yml 以及 spring.factories 中的 BootstrapConfiguration 等,用户可以自定义 Spring Cloud 上下文的配置类。
  3. prepareContext

    • 准备 Spring Boot 上下文,将触发所有 ApplicationContextInitializer 来初始化上下文。
    • 其中的 PropertySourceBootstrapConfiguration 实现 initialize 方法,遍历所有的属性源加载器 PropertySourceLocatorslocate 方法,用于读取外部化配置,解析成 PropertySource 并组装到上下文的 Environment
    • 用户可以自定义 PropertySourceLocator 来加载自定义属性。

    注意Environment 中的 PropertySources 是一个列表,保存了环境变量、命令行参数、application.ymlbootstrap.yml 的属性值配置;排序越靠前,最终初始化 Bean 时使用的优先级越高。自定义 PropertySourceLocator 加载的 PropertySource 将被 addFirst,即优先级高于其他配置。

  4. refreshContext

    • 刷新 SpringApplicationContext,完成 IOC 容器的 Bean 创建等 Spring Framework 工作。
    • 可以注册 ApplicationPreparedEvent 监听器,当应用准备完成时处理一些基于 Spring Boot 上下文的工作。
Spring Boot 的属性值绑定
  • @EnableConfigurationProperties 注解引入了 EnableConfigurationPropertiesImportSelector 后置处理器。

  • EnableConfigurationPropertiesImportSelector 后置处理器向 Spring 容器中注册了 ConfigurationPropertiesBeanRegistrarConfigurationPropertiesBindingPostProcessorRegistrar 两个 Bean。

  • ConfigurationPropertiesBeanRegistrar 向 Spring 容器中注册了 XxxProperties 类型的 Bean。

  • ConfigurationPropertiesBindingPostProcessorRegistrar 向 Spring 容器中注册了 ConfigurationBeanFactoryMetadataConfigurationPropertiesBindingPostProcessor 两个后置处理器。

    • ConfigurationBeanFactoryMetadata 后置处理器在初始化 Bean Factory 时将 @Bean 注解的元数据存储起来,以便在后续的外部配置属性绑定中使用。
    • ConfigurationPropertiesBindingPostProcessor 后置处理器将外部配置属性值绑定到 XxxProperties 类属性的逻辑委托给 ConfigurationPropertiesBinder 对象,后者再将属性绑定的逻辑委托给 Binder 对象完成。

参考:掘金文章

2. 如何实现重绑定

  • ConfigurationPropertiesRebinder:Spring Cloud 的组件,监听 EnvironmentChangeEvent,对 @ConfigurationProperties 的 Bean 进行重绑定。

3. 如何动态刷新日志级别

  • LoggingRebinder:Spring Cloud 的组件,监听 EnvironmentChangeEvent,动态刷新 Logger 级别。

TCC 工作原理

Java SDK 源码:TCC Client V2

TccAutoConfiguration

spring.factories 指定的配置类 TccAutoConfiguration 在容器早期,将 DynamicConfigAnnotationProcessor 注册到上下文。

java

Copy

@Configuration
@Import(TccAutoConfiguration.Register.class)
public class TccAutoConfiguration {

    public static class Register implements ImportBeanDefinitionRegistrar {

        @Override
        public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
            if (!registry.containsBeanDefinition(DynamicConfigAnnotationProcessor.BEAN_NAME)) {
                GenericBeanDefinition definition = new GenericBeanDefinition();
                definition.setBeanClass(DynamicConfigAnnotationProcessor.class);
                definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
                definition.setSynthetic(true);
                registry.registerBeanDefinition(DynamicConfigAnnotationProcessor.BEAN_NAME, definition);
            }
        }
    }
}

DynamicConfigAnnotationProcessor

DynamicConfigAnnotationProcessor 实现了 BeanPostProcessorBeanFactoryPostProcessor 接口。

  • BeanFactoryPostProcessor:作为 Bean 工厂的后置处理器,在 Bean Factory 创建完成后执行一些自定义操作,此处通过 Bean Factory 包装了自定义的 StringValueResolver
  • BeanPostProcessor:实现 postProcessBeforeInitialization 方法,在 Bean 初始化之前,读取 TCC 配置,针对用户自定义 Bean 中 @DynamicConfig 注解的属性进行设置,并注册配置变更监听方法。

TccConfigClient

  • 初始化全局静态成员 AbstractDataLoader 中的 TCC 元数据。
  • 其线程池定时每 10 秒拉取 TCC 配置,检测到变化时通过异步线程触发 Listener 的 onLoad 方法。

写在最后

有问题或想法欢迎交流~

欢迎共建,代码仓库:Tcc-plus 仓库

参考文献

许可证

本项目采用 MIT 许可证 进行许可。

贡献

欢迎贡献代码!请参阅 贡献指南 了解更多详情。

联系我们

若有任何问题或建议,请通过以下方式联系我们:

致谢

感谢所有贡献者和使用者,你们的支持是我们前进的动力!

版本历史

  • v1.0.0 - 初始发布

结束语

感谢您阅读本文档,希望 Tcc-plus 能为您的项目带来帮助!

代码示例

以下是一个简单的 Spring Boot 应用集成 Tcc-plus 的示例:

java

Copy

@SpringBootApplication
@EnableConfigurationProperties(MyProperties.class)
public class TccPlusApplication {

    public static void main(String[] args) {
        SpringApplication.run(TccPlusApplication.class, args);
    }
}

@ConfigurationProperties(prefix = "my.config")
public class MyProperties {
    private String name;
    private int timeout;

    // Getters and Setters
}

application.yml 中:

yaml

Copy

my:
  config:
    name: "defaultName"
    timeout: 30

当 Tcc-plus 监测到配置变更时,MyProperties 将自动重绑定为最新配置。

常见问题

:Tcc-plus 如何保证配置安全性?

:Tcc-plus 利用 TCC 的权限管理功能,对敏感配置进行权限控制,确保只有授权的应用和用户可以访问和修改配置。

:Tcc-plus 支持哪些格式的配置文件?

:Tcc-plus 支持 propertiesyamljson 格式的配置解析。

贡献指南

欢迎各位开发者为 Tcc-plus 做出贡献!请按照以下步骤进行:

  1. Fork 本仓库。
  2. 创建您的特性分支:git checkout -b feature/YourFeature
  3. 提交您的更改:git commit -m "Add some feature"
  4. 推送到分支:git push origin feature/YourFeature
  5. 创建一个 Pull Request

详细信息请参阅 贡献指南

许可证

本项目采用 MIT 许可证,详见 LICENSE

联系方式

如有任何问题或建议,欢迎通过以下方式与我们联系:

致谢

感谢所有贡献者的辛勤工作和支持!

结束语

感谢您的关注和支持,期待 Tcc-plus 在实际项目中为您带来便利!

免责声明

本项目仅供学习和研究使用,使用前请确保理解其功能和限制。

相关资源

Example Usage

Here’s an example of how to use Tcc-plus in a Spring Boot application:

java

Copy

@RestController
public class ConfigController {

    @Autowired
    private MyProperties myProperties;

    @GetMapping("/config")
    public MyProperties getConfig() {
        return myProperties;
    }
}

When the configuration changes in TCC, MyProperties will automatically update without requiring a restart.

Future Work

  • 扩展支持:支持更多类型的配置绑定。
  • 性能优化:优化配置变更的检测和应用速度。
  • 文档完善:提供更详细的教程和示例。

Feedback

欢迎通过 GitHub Issues 提交您的反馈和建议。

最后声明

本文档为项目的非正式文档,可能随时更新。请以实际项目中的文档为准。

版本日志

[v1.0.0] - 2023-10-01

初始发布。