Skip to main content

配置中心详解

在微服务架构中,配置管理是一个重要的挑战。配置中心提供了集中化的配置管理能力,支持配置的动态刷新、多环境隔离、灰度发布等功能,是微服务架构的重要组成部分。

核心价值

配置中心 = 集中管理 + 动态刷新 + 多环境隔离 + 灰度发布 + 安全审计

配置中心架构

配置中心方案对比

yaml
1# Config Server配置
2spring:
3 application:
4 name: config-server
5 cloud:
6 config:
7 server:
8 git:
9 uri: https://github.com/your-org/config-repo
10 username: your-username
11 password: your-password
12 search-paths: config
13 default-label: main
14 clone-on-start: true

特点

  • ✅ Spring生态集成度高
  • ✅ 支持Git、SVN等多种存储
  • ✅ 支持配置加密
  • ❌ 功能相对简单
  • ❌ 界面管理能力弱

1. 配置中心基础概念

1.1 什么是配置中心?

配置中心是微服务架构中的一个重要组件,它负责管理所有服务的配置信息,提供配置的统一存储、分发、更新和版本管理功能。

配置中心的核心功能

配置中心核心功能示例
java
1public class ConfigCenterFeatures {
2 /*
3 * 配置中心的核心功能
4 * 1. 配置存储:集中存储所有服务的配置信息
5 * 2. 配置分发:将配置信息分发给各个服务
6 * 3. 动态刷新:支持配置的动态更新和实时生效
7 * 4. 版本管理:管理配置的版本历史和回滚
8 * 5. 环境隔离:支持多环境配置隔离
9 * 6. 安全控制:提供配置的访问控制和加密
10 */
11
12 // 配置项定义
13 public class ConfigItem {
14 private String key; // 配置键
15 private String value; // 配置值
16 private String description; // 配置描述
17 private String type; // 配置类型
18 private boolean encrypted; // 是否加密
19 private String environment; // 环境
20 private String application; // 应用名称
21 private long version; // 版本号
22 private long createTime; // 创建时间
23 private long updateTime; // 更新时间
24 private String createdBy; // 创建人
25 private String updatedBy; // 更新人
26
27 // 构造函数、getter、setter方法
28 }
29
30 // 配置存储接口
31 public interface ConfigStorage {
32 // 存储配置
33 void store(String application, String environment, String key, String value);
34
35 // 获取配置
36 String get(String application, String environment, String key);
37
38 // 删除配置
39 void delete(String application, String environment, String key);
40
41 // 获取所有配置
42 Map<String, String> getAll(String application, String environment);
43
44 // 检查配置是否存在
45 boolean exists(String application, String environment, String key);
46 }
47
48 // 配置分发接口
49 public interface ConfigDistribution {
50 // 分发配置到指定应用
51 void distribute(String application, String environment, Map<String, String> configs);
52
53 // 广播配置变更
54 void broadcast(String application, String environment, String key, String value);
55
56 // 订阅配置变更
57 void subscribe(String application, String environment, ConfigChangeListener listener);
58
59 // 取消订阅
60 void unsubscribe(String application, String environment, ConfigChangeListener listener);
61 }
62
63 // 配置变更监听器
64 public interface ConfigChangeListener {
65 // 配置变更回调
66 void onChange(String key, String oldValue, String newValue);
67
68 // 批量配置变更回调
69 void onBatchChange(Map<String, ConfigChange> changes);
70 }
71
72 // 配置变更事件
73 public class ConfigChange {
74 private String key;
75 private String oldValue;
76 private String newValue;
77 private ChangeType changeType;
78
79 public enum ChangeType {
80 ADD, UPDATE, DELETE
81 }
82
83 // 构造函数、getter、setter方法
84 }
85}

1.2 配置中心架构模式

推拉结合模式

推拉结合模式示例
java
1public class PushPullArchitecture {
2 /*
3 * 推拉结合模式
4 * 1. 启动时拉取:服务启动时从配置中心拉取配置
5 * 2. 运行时推送:配置变更时主动推送给服务
6 * 3. 定时拉取:定期拉取配置确保一致性
7 * 4. 长轮询:使用长轮询实现实时推送
8 */
9
10 // 配置客户端
11 public class ConfigClient {
12 private final ConfigStorage configStorage;
13 private final ConfigDistribution configDistribution;
14 private final Map<String, String> localConfig = new ConcurrentHashMap<>();
15 private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
16
17 public ConfigClient(ConfigStorage configStorage, ConfigDistribution configDistribution) {
18 this.configStorage = configStorage;
19 this.configDistribution = configDistribution;
20
21 // 启动时拉取配置
22 pullConfigOnStartup();
23
24 // 订阅配置变更
25 subscribeConfigChanges();
26
27 // 定时拉取配置
28 schedulePeriodicPull();
29 }
30
31 // 启动时拉取配置
32 private void pullConfigOnStartup() {
33 Map<String, String> configs = configStorage.getAll(getApplicationName(), getEnvironment());
34 localConfig.putAll(configs);
35 }
36
37 // 订阅配置变更
38 private void subscribeConfigChanges() {
39 configDistribution.subscribe(getApplicationName(), getEnvironment(), new ConfigChangeListener() {
40 @Override
41 public void onChange(String key, String oldValue, String newValue) {
42 localConfig.put(key, newValue);
43 notifyConfigChange(key, oldValue, newValue);
44 }
45
46 @Override
47 public void onBatchChange(Map<String, ConfigChange> changes) {
48 for (Map.Entry<String, ConfigChange> entry : changes.entrySet()) {
49 ConfigChange change = entry.getValue();
50 localConfig.put(change.getKey(), change.getNewValue());
51 }
52 notifyBatchConfigChange(changes);
53 }
54 });
55 }
56
57 // 定时拉取配置
58 private void schedulePeriodicPull() {
59 scheduler.scheduleAtFixedRate(() -> {
60 try {
61 Map<String, String> remoteConfigs = configStorage.getAll(getApplicationName(), getEnvironment());
62 Map<String, String> changes = new HashMap<>();
63
64 for (Map.Entry<String, String> entry : remoteConfigs.entrySet()) {
65 String key = entry.getKey();
66 String newValue = entry.getValue();
67 String oldValue = localConfig.get(key);
68
69 if (!Objects.equals(oldValue, newValue)) {
70 changes.put(key, newValue);
71 localConfig.put(key, newValue);
72 }
73 }
74
75 if (!changes.isEmpty()) {
76 notifyBatchConfigChange(changes);
77 }
78 } catch (Exception e) {
79 log.error("Failed to pull config", e);
80 }
81 }, 30, 30, TimeUnit.SECONDS);
82 }
83
84 // 获取配置
85 public String getConfig(String key) {
86 return localConfig.get(key);
87 }
88
89 // 获取配置(带默认值)
90 public String getConfig(String key, String defaultValue) {
91 return localConfig.getOrDefault(key, defaultValue);
92 }
93
94 // 获取所有配置
95 public Map<String, String> getAllConfig() {
96 return new HashMap<>(localConfig);
97 }
98
99 // 通知配置变更
100 private void notifyConfigChange(String key, String oldValue, String newValue) {
101 // 通知应用配置变更
102 ApplicationContext context = getApplicationContext();
103 if (context != null) {
104 context.publishEvent(new ConfigChangeEvent(key, oldValue, newValue));
105 }
106 }
107
108 // 通知批量配置变更
109 private void notifyBatchConfigChange(Map<String, String> changes) {
110 // 通知应用批量配置变更
111 ApplicationContext context = getApplicationContext();
112 if (context != null) {
113 context.publishEvent(new BatchConfigChangeEvent(changes));
114 }
115 }
116 }
117
118 // 长轮询实现
119 public class LongPollingClient {
120 private final String configServerUrl;
121 private final HttpClient httpClient;
122 private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
123
124 public LongPollingClient(String configServerUrl) {
125 this.configServerUrl = configServerUrl;
126 this.httpClient = HttpClient.create();
127 }
128
129 // 开始长轮询
130 public void startLongPolling() {
131 scheduler.submit(this::doLongPolling);
132 }
133
134 // 执行长轮询
135 private void doLongPolling() {
136 while (!Thread.currentThread().isInterrupted()) {
137 try {
138 // 发送长轮询请求
139 String response = httpClient.get()
140 .uri(configServerUrl + "/config/long-polling")
141 .retrieve()
142 .bodyToMono(String.class)
143 .timeout(Duration.ofSeconds(30))
144 .block();
145
146 // 处理响应
147 if (response != null) {
148 handleConfigChange(response);
149 }
150 } catch (Exception e) {
151 log.error("Long polling failed", e);
152 // 等待一段时间后重试
153 Thread.sleep(5000);
154 }
155 }
156 }
157
158 // 处理配置变更
159 private void handleConfigChange(String response) {
160 // 解析响应并处理配置变更
161 ConfigChangeEvent event = parseConfigChange(response);
162 if (event != null) {
163 notifyConfigChange(event);
164 }
165 }
166
167 // 解析配置变更
168 private ConfigChangeEvent parseConfigChange(String response) {
169 // 解析响应JSON
170 try {
171 ObjectMapper mapper = new ObjectMapper();
172 return mapper.readValue(response, ConfigChangeEvent.class);
173 } catch (Exception e) {
174 log.error("Failed to parse config change", e);
175 return null;
176 }
177 }
178 }
179}

1.3 配置中心优势与挑战

核心优势

配置中心优势示例
java
1public class ConfigCenterAdvantages {
2 /*
3 * 配置中心的核心优势
4 * 1. 集中管理:所有配置集中存储,便于管理
5 * 2. 动态刷新:支持配置的动态更新,无需重启服务
6 * 3. 环境隔离:支持多环境配置隔离
7 * 4. 版本管理:支持配置的版本历史和回滚
8 * 5. 安全控制:提供配置的访问控制和加密
9 * 6. 审计追踪:记录配置的变更历史
10 */
11
12 // 集中管理示例
13 public class CentralizedManagement {
14 private final ConfigStorage configStorage;
15
16 public void manageConfigs() {
17 // 统一管理所有应用的配置
18 String[] applications = {"user-service", "order-service", "payment-service"};
19 String[] environments = {"dev", "test", "prod"};
20
21 for (String app : applications) {
22 for (String env : environments) {
23 // 批量更新配置
24 updateConfigs(app, env);
25 // 批量验证配置
26 validateConfigs(app, env);
27 // 批量发布配置
28 publishConfigs(app, env);
29 }
30 }
31 }
32
33 private void updateConfigs(String application, String environment) {
34 // 更新配置逻辑
35 Map<String, String> configs = new HashMap<>();
36 configs.put("database.url", "jdbc:mysql://localhost:3306/" + application);
37 configs.put("redis.host", "localhost");
38 configs.put("redis.port", "6379");
39
40 for (Map.Entry<String, String> entry : configs.entrySet()) {
41 configStorage.store(application, environment, entry.getKey(), entry.getValue());
42 }
43 }
44 }
45
46 // 动态刷新示例
47 public class DynamicRefresh {
48 private final ConfigClient configClient;
49 private final ApplicationContext applicationContext;
50
51 public void refreshConfig(String key, String newValue) {
52 // 更新配置
53 configClient.updateConfig(key, newValue);
54
55 // 通知应用刷新
56 applicationContext.publishEvent(new RefreshEvent());
57
58 // 记录刷新日志
59 log.info("Config refreshed: {} = {}", key, newValue);
60 }
61
62 // 批量刷新
63 public void batchRefresh(Map<String, String> configs) {
64 for (Map.Entry<String, String> entry : configs.entrySet()) {
65 refreshConfig(entry.getKey(), entry.getValue());
66 }
67 }
68 }
69
70 // 环境隔离示例
71 public class EnvironmentIsolation {
72 private final ConfigStorage configStorage;
73
74 public void isolateEnvironments() {
75 // 开发环境配置
76 Map<String, String> devConfigs = new HashMap<>();
77 devConfigs.put("database.url", "jdbc:mysql://dev-db:3306/app");
78 devConfigs.put("log.level", "DEBUG");
79 devConfigs.put("feature.enabled", "true");
80
81 // 测试环境配置
82 Map<String, String> testConfigs = new HashMap<>();
83 testConfigs.put("database.url", "jdbc:mysql://test-db:3306/app");
84 testConfigs.put("log.level", "INFO");
85 testConfigs.put("feature.enabled", "true");
86
87 // 生产环境配置
88 Map<String, String> prodConfigs = new HashMap<>();
89 prodConfigs.put("database.url", "jdbc:mysql://prod-db:3306/app");
90 prodConfigs.put("log.level", "WARN");
91 prodConfigs.put("feature.enabled", "false");
92
93 // 存储不同环境的配置
94 storeConfigs("user-service", "dev", devConfigs);
95 storeConfigs("user-service", "test", testConfigs);
96 storeConfigs("user-service", "prod", prodConfigs);
97 }
98
99 private void storeConfigs(String application, String environment, Map<String, String> configs) {
100 for (Map.Entry<String, String> entry : configs.entrySet()) {
101 configStorage.store(application, environment, entry.getKey(), entry.getValue());
102 }
103 }
104 }
105}

主要挑战

配置中心挑战示例
java
1public class ConfigCenterChallenges {
2 /*
3 * 配置中心面临的主要挑战
4 * 1. 配置一致性:确保所有服务实例的配置一致
5 * 2. 配置安全性:保护敏感配置信息
6 * 3. 配置性能:快速响应配置查询和更新
7 * 4. 配置依赖:处理配置项之间的依赖关系
8 * 5. 配置冲突:解决配置冲突和优先级问题
9 * 6. 配置回滚:支持配置的快速回滚
10 */
11
12 // 配置一致性
13 public class ConfigConsistency {
14 private final ConfigStorage configStorage;
15 private final ConfigDistribution configDistribution;
16
17 public void ensureConsistency(String application, String environment) {
18 // 获取所有服务实例
19 List<String> instances = getServiceInstances(application);
20
21 // 获取当前配置
22 Map<String, String> currentConfig = configStorage.getAll(application, environment);
23
24 // 向所有实例推送配置
25 for (String instance : instances) {
26 try {
27 configDistribution.distribute(instance, environment, currentConfig);
28 } catch (Exception e) {
29 log.error("Failed to distribute config to instance: {}", instance, e);
30 // 记录不一致的实例
31 recordInconsistentInstance(instance);
32 }
33 }
34
35 // 验证配置一致性
36 validateConsistency(application, environment);
37 }
38
39 private void validateConsistency(String application, String environment) {
40 List<String> instances = getServiceInstances(application);
41 Map<String, String> expectedConfig = configStorage.getAll(application, environment);
42
43 for (String instance : instances) {
44 Map<String, String> actualConfig = getInstanceConfig(instance, environment);
45 if (!Objects.equals(expectedConfig, actualConfig)) {
46 log.warn("Config inconsistency detected for instance: {}", instance);
47 // 触发配置同步
48 syncConfig(instance, environment, expectedConfig);
49 }
50 }
51 }
52 }
53
54 // 配置安全性
55 public class ConfigSecurity {
56 private final EncryptionService encryptionService;
57 private final AccessControlService accessControlService;
58
59 public void secureConfig(String key, String value) {
60 // 检查是否需要加密
61 if (isSensitiveConfig(key)) {
62 value = encryptionService.encrypt(value);
63 }
64
65 // 检查访问权限
66 if (!accessControlService.hasPermission(getCurrentUser(), key)) {
67 throw new AccessDeniedException("No permission to access config: " + key);
68 }
69
70 // 记录访问日志
71 logAccess(key, "READ");
72 }
73
74 public String getSecureConfig(String key) {
75 // 检查访问权限
76 if (!accessControlService.hasPermission(getCurrentUser(), key)) {
77 throw new AccessDeniedException("No permission to access config: " + key);
78 }
79
80 // 获取配置值
81 String value = configStorage.get(getApplicationName(), getEnvironment(), key);
82
83 // 解密敏感配置
84 if (isSensitiveConfig(key)) {
85 value = encryptionService.decrypt(value);
86 }
87
88 // 记录访问日志
89 logAccess(key, "READ");
90
91 return value;
92 }
93
94 private boolean isSensitiveConfig(String key) {
95 return key.contains("password") ||
96 key.contains("secret") ||
97 key.contains("token") ||
98 key.contains("key");
99 }
100 }
101}
配置中心设计原则
  1. 集中管理:所有配置集中存储和管理
  2. 动态刷新:支持配置的动态更新和实时生效
  3. 环境隔离:支持多环境配置隔离
  4. 安全控制:提供配置的访问控制和加密
  5. 版本管理:支持配置的版本历史和回滚
  6. 性能优化:确保配置查询和更新的高性能

2. Spring Cloud Config 详解

2.1 Spring Cloud Config 架构

Spring Cloud Config是Spring Cloud生态中的配置中心解决方案,支持配置的集中管理、动态刷新和环境隔离。

Config Server 架构

Config Server架构示例
java
1public class ConfigServerArchitecture {
2 /*
3 * Config Server核心组件
4 * 1. 配置存储:支持Git、SVN、文件系统等存储方式
5 * 2. 配置加密:支持对称和非对称加密
6 * 3. 配置刷新:支持配置的动态刷新
7 * 4. 健康检查:提供健康检查端点
8 * 5. 安全控制:支持认证和授权
9 */
10
11 // Config Server配置
12 public class ConfigServerConfig {
13 private final String gitUri; // Git仓库地址
14 private final String gitUsername; // Git用户名
15 private final String gitPassword; // Git密码
16 private final String searchPaths; // 搜索路径
17 private final String defaultLabel; // 默认分支
18 private final boolean cloneOnStart; // 启动时克隆
19 private final int timeout; // 超时时间
20 private final boolean forcePull; // 强制拉取
21
22 public ConfigServerConfig(String gitUri, String gitUsername, String gitPassword) {
23 this.gitUri = gitUri;
24 this.gitUsername = gitUsername;
25 this.gitPassword = gitPassword;
26 this.searchPaths = "config";
27 this.defaultLabel = "main";
28 this.cloneOnStart = true;
29 this.timeout = 5000;
30 this.forcePull = false;
31 }
32 }
33
34 // 配置存储实现
35 public class GitConfigStorage implements ConfigStorage {
36 private final GitRepository gitRepository;
37 private final Map<String, Map<String, String>> configCache = new ConcurrentHashMap<>();
38
39 public GitConfigStorage(String gitUri, String username, String password) {
40 this.gitRepository = new GitRepository(gitUri, username, password);
41 }
42
43 @Override
44 public String get(String application, String environment, String key) {
45 String cacheKey = application + "-" + environment;
46 Map<String, String> configs = configCache.get(cacheKey);
47
48 if (configs == null) {
49 configs = loadConfigsFromGit(application, environment);
50 configCache.put(cacheKey, configs);
51 }
52
53 return configs.get(key);
54 }
55
56 @Override
57 public Map<String, String> getAll(String application, String environment) {
58 String cacheKey = application + "-" + environment;
59 Map<String, String> configs = configCache.get(cacheKey);
60
61 if (configs == null) {
62 configs = loadConfigsFromGit(application, environment);
63 configCache.put(cacheKey, configs);
64 }
65
66 return new HashMap<>(configs);
67 }
68
69 private Map<String, String> loadConfigsFromGit(String application, String environment) {
70 // 从Git加载配置
71 String configPath = String.format("%s/%s-%s.yml", searchPaths, application, environment);
72 return gitRepository.readFile(configPath);
73 }
74
75 public void refreshCache(String application, String environment) {
76 String cacheKey = application + "-" + environment;
77 configCache.remove(cacheKey);
78 }
79 }
80}

2.2 Config Server 配置与部署

基础配置

application.yml
yaml
1spring:
2 application:
3 name: config-server
4 cloud:
5 config:
6 server:
7 git:
8 uri: https://github.com/your-org/config-repo
9 username: your-username
10 password: your-password
11 search-paths: config
12 default-label: main
13 clone-on-start: true
14 timeout: 5000
15 force-pull: false
16 # 支持多个Git仓库
17 repos:
18 user-service:
19 uri: https://github.com/your-org/user-service-config
20 search-paths: config
21 order-service:
22 uri: https://github.com/your-org/order-service-config
23 search-paths: config
24 # 文件系统存储
25 native:
26 search-locations: classpath:/config
27 # 加密配置
28 encrypt:
29 enabled: true
30 key: your-encryption-key
31 # 健康检查
32 health:
33 repositories:
34 user-service:
35 label: main
36 name: user-service
37 profiles: dev
38server:
39 port: 8888
40
41# 安全配置
42security:
43 basic:
44 enabled: true
45 user:
46 name: admin
47 password: admin123
48
49# 监控配置
50management:
51 endpoints:
52 web:
53 exposure:
54 include: health,info,metrics,refresh
55 endpoint:
56 health:
57 show-details: always

启动类

Config Server启动类
java
1@EnableConfigServer
2@SpringBootApplication
3public class ConfigServerApplication {
4
5 public static void main(String[] args) {
6 SpringApplication.run(ConfigServerApplication.class, args);
7 }
8
9 @Bean
10 public ConfigServerProperties configServerProperties() {
11 ConfigServerProperties properties = new ConfigServerProperties();
12 properties.setDefaultLabel("main");
13 properties.setCloneOnStart(true);
14 return properties;
15 }
16
17 @Bean
18 public GitConfigRepository gitConfigRepository() {
19 GitConfigRepository repository = new GitConfigRepository();
20 repository.setUri("https://github.com/your-org/config-repo");
21 repository.setSearchPaths("config");
22 return repository;
23 }
24
25 @Bean
26 public EncryptionController encryptionController() {
27 return new EncryptionController();
28 }
29
30 @Bean
31 public DecryptionController decryptionController() {
32 return new DecryptionController();
33 }
34}

2.3 Config Client 配置与使用

客户端配置

bootstrap.yml
yaml
1spring:
2 application:
3 name: user-service
4 cloud:
5 config:
6 uri: http://localhost:8888
7 profile: dev
8 label: main
9 # 失败快速返回
10 fail-fast: true
11 # 重试配置
12 retry:
13 initial-interval: 1000
14 max-interval: 2000
15 max-attempts: 6
16 multiplier: 1.1
17 # 请求超时
18 request-read-timeout: 5000
19 # 连接超时
20 connect-timeout: 3000
21 # 用户名密码
22 username: admin
23 password: admin123
24 # 配置刷新
25 refresh-enabled: true
26 # 配置优先级
27 override-system-properties: false
28 # 配置覆盖
29 override-none: false
30 # 配置前缀
31 name: ${spring.application.name}
32 # 配置后缀
33 profile: ${spring.profiles.active:default}
34 # 配置标签
35 label: ${spring.cloud.config.label:main}

客户端启动类

Config Client启动类
java
1@EnableDiscoveryClient
2@SpringBootApplication
3public class UserServiceApplication {
4
5 public static void main(String[] args) {
6 SpringApplication.run(UserServiceApplication.class, args);
7 }
8
9 @Bean
10 @LoadBalanced
11 public RestTemplate restTemplate() {
12 return new RestTemplate();
13 }
14
15 @Bean
16 public ConfigClientProperties configClientProperties() {
17 ConfigClientProperties properties = new ConfigClientProperties();
18 properties.setUri("http://localhost:8888");
19 properties.setFailFast(true);
20 properties.setRequestReadTimeout(5000);
21 properties.setConnectTimeout(3000);
22 return properties;
23 }
24}

配置使用示例

配置使用示例
java
1@RestController
2@RefreshScope
3public class ConfigController {
4
5 @Value("${feature.enabled:false}")
6 private boolean featureEnabled;
7
8 @Value("${database.url}")
9 private String databaseUrl;
10
11 @Value("${redis.host:localhost}")
12 private String redisHost;
13
14 @Value("${redis.port:6379}")
15 private int redisPort;
16
17 @Autowired
18 private Environment environment;
19
20 @Autowired
21 private ConfigurationProperties configProperties;
22
23 @GetMapping("/config/feature")
24 public String getFeatureConfig() {
25 return "Feature enabled: " + featureEnabled;
26 }
27
28 @GetMapping("/config/database")
29 public String getDatabaseConfig() {
30 return "Database URL: " + databaseUrl;
31 }
32
33 @GetMapping("/config/redis")
34 public String getRedisConfig() {
35 return String.format("Redis: %s:%d", redisHost, redisPort);
36 }
37
38 @GetMapping("/config/all")
39 public Map<String, Object> getAllConfig() {
40 Map<String, Object> configs = new HashMap<>();
41 configs.put("feature.enabled", featureEnabled);
42 configs.put("database.url", databaseUrl);
43 configs.put("redis.host", redisHost);
44 configs.put("redis.port", redisPort);
45 return configs;
46 }
47
48 @GetMapping("/config/env/{key}")
49 public String getConfigByKey(@PathVariable String key) {
50 return environment.getProperty(key, "Not found");
51 }
52
53 @PostMapping("/config/refresh")
54 public String refreshConfig() {
55 // 手动刷新配置
56 return "Config refreshed successfully";
57 }
58}
59
60// 配置属性类
61@ConfigurationProperties(prefix = "app")
62@Data
63public class AppConfigProperties {
64 private String name;
65 private String version;
66 private DatabaseConfig database;
67 private RedisConfig redis;
68 private FeatureConfig feature;
69
70 @Data
71 public static class DatabaseConfig {
72 private String url;
73 private String username;
74 private String password;
75 private int maxConnections;
76 }
77
78 @Data
79 public static class RedisConfig {
80 private String host;
81 private int port;
82 private String password;
83 private int database;
84 }
85
86 @Data
87 public static class FeatureConfig {
88 private boolean enabled;
89 private String description;
90 }
91}

2.4 配置加密与安全

加密配置

加密配置示例
java
1public class EncryptionConfiguration {
2
3 // 对称加密
4 public class SymmetricEncryption {
5 private final String key;
6 private final Cipher cipher;
7
8 public SymmetricEncryption(String key) throws Exception {
9 this.key = key;
10 this.cipher = Cipher.getInstance("AES");
11 }
12
13 public String encrypt(String value) throws Exception {
14 SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES");
15 cipher.init(Cipher.ENCRYPT_MODE, secretKey);
16 byte[] encryptedBytes = cipher.doFinal(value.getBytes());
17 return Base64.getEncoder().encodeToString(encryptedBytes);
18 }
19
20 public String decrypt(String encryptedValue) throws Exception {
21 SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES");
22 cipher.init(Cipher.DECRYPT_MODE, secretKey);
23 byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedValue));
24 return new String(decryptedBytes);
25 }
26 }
27
28 // 非对称加密
29 public class AsymmetricEncryption {
30 private final PrivateKey privateKey;
31 private final PublicKey publicKey;
32
33 public AsymmetricEncryption() throws Exception {
34 KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
35 keyGen.initialize(2048);
36 KeyPair pair = keyGen.generateKeyPair();
37 this.privateKey = pair.getPrivate();
38 this.publicKey = pair.getPublic();
39 }
40
41 public String encrypt(String value) throws Exception {
42 Cipher cipher = Cipher.getInstance("RSA");
43 cipher.init(Cipher.ENCRYPT_MODE, publicKey);
44 byte[] encryptedBytes = cipher.doFinal(value.getBytes());
45 return Base64.getEncoder().encodeToString(encryptedBytes);
46 }
47
48 public String decrypt(String encryptedValue) throws Exception {
49 Cipher cipher = Cipher.getInstance("RSA");
50 cipher.init(Cipher.DECRYPT_MODE, privateKey);
51 byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedValue));
52 return new String(decryptedBytes);
53 }
54 }
55
56 // 配置加密控制器
57 @RestController
58 public class EncryptionController {
59
60 @Autowired
61 private TextEncryptor textEncryptor;
62
63 @PostMapping("/encrypt")
64 public String encrypt(@RequestBody String value) {
65 return textEncryptor.encrypt(value);
66 }
67
68 @PostMapping("/decrypt")
69 public String decrypt(@RequestBody String encryptedValue) {
70 return textEncryptor.decrypt(encryptedValue);
71 }
72 }
73}
Spring Cloud Config最佳实践
  1. 配置分离:将不同环境的配置分离到不同的分支或目录
  2. 配置加密:对敏感配置进行加密
  3. 配置刷新:合理使用配置刷新机制
  4. 监控告警:监控配置中心的健康状况
  5. 备份恢复:定期备份配置数据

3. 其他配置中心方案

3.1 Apollo 配置中心

Apollo是携程开源的配置中心,支持配置的实时推送、灰度发布、权限管理等功能。

Apollo 特点

Apollo特点
java
1public class ApolloFeatures {
2 /*
3 * Apollo核心特点
4 * 1. 实时推送:支持配置的实时推送
5 * 2. 灰度发布:支持配置的灰度发布
6 * 3. 权限管理:支持细粒度的权限控制
7 * 4. 审计日志:记录配置的变更历史
8 * 5. 多环境:支持多环境配置管理
9 * 6. 客户端缓存:支持客户端本地缓存
10 */
11}

Apollo 配置

apollo.properties
properties
1# Apollo配置
2app.id=user-service
3apollo.meta=http://localhost:8080
4apollo.bootstrap.enabled=true
5apollo.bootstrap.eagerLoad.enabled=true
6apollo.bootstrap.namespaces=application,user-service
7apollo.cache.enabled=true
8apollo.cache.dir=/opt/data/apollo-cache
9apollo.refreshInterval=3000
10apollo.longPollingInitialDelayInMills=1000
11apollo.longPollingTimeoutInSeconds=90
12apollo.autoUpdateInjectedSpringProperties=true
13apollo.property.order.enabled=true
14apollo.property.order.enabled=true

3.2 Nacos Config

Nacos是阿里巴巴开源的配置中心,支持配置的动态更新、多环境隔离、灰度发布等功能。

Nacos Config 配置

nacos-config.yml
yaml
1spring:
2 application:
3 name: order-service
4 cloud:
5 nacos:
6 config:
7 server-addr: localhost:8848
8 file-extension: yaml
9 group: DEFAULT_GROUP
10 namespace: public
11 # 配置刷新
12 refresh-enabled: true
13 # 配置优先级
14 shared-configs:
15 - data-id: common-config.yaml
16 group: DEFAULT_GROUP
17 refresh: true
18 - data-id: database-config.yaml
19 group: DEFAULT_GROUP
20 refresh: true
21 # 扩展配置
22 extension-configs:
23 - data-id: extension-config.yaml
24 group: DEFAULT_GROUP
25 refresh: true
26 # 配置监听
27 config-long-poll-timeout: 460
28 config-retry-time: 3000
29 max-retry: 3
30 enable-remote-sync-config: true

4. 配置优先级与隔离

4.1 配置优先级

优先级规则

配置优先级示例
java
1public class ConfigPriority {
2 /*
3 * 配置优先级(从高到低)
4 * 1. 命令行参数:--spring.profiles.active=prod
5 * 2. 系统属性:-Dspring.profiles.active=prod
6 * 3. 环境变量:SPRING_PROFILES_ACTIVE=prod
7 * 4. 配置文件:application-prod.yml
8 * 5. 默认配置:application.yml
9 */
10
11 // 配置加载顺序
12 public class ConfigLoadingOrder {
13 private final List<ConfigSource> configSources = Arrays.asList(
14 new CommandLineConfigSource(), // 命令行参数
15 new SystemPropertyConfigSource(), // 系统属性
16 new EnvironmentConfigSource(), // 环境变量
17 new RemoteConfigSource(), // 远程配置
18 new LocalConfigSource() // 本地配置
19 );
20
21 public Map<String, String> loadConfig() {
22 Map<String, String> finalConfig = new HashMap<>();
23
24 // 按优先级加载配置
25 for (ConfigSource source : configSources) {
26 Map<String, String> config = source.loadConfig();
27 // 高优先级配置覆盖低优先级配置
28 finalConfig.putAll(config);
29 }
30
31 return finalConfig;
32 }
33 }
34
35 // 配置源接口
36 public interface ConfigSource {
37 Map<String, String> loadConfig();
38 int getPriority(); // 优先级,数字越大优先级越高
39 }
40
41 // 命令行配置源
42 public class CommandLineConfigSource implements ConfigSource {
43 @Override
44 public Map<String, String> loadConfig() {
45 Map<String, String> config = new HashMap<>();
46 // 解析命令行参数
47 String[] args = System.getProperty("sun.java.command").split(" ");
48 for (String arg : args) {
49 if (arg.startsWith("--")) {
50 String[] parts = arg.substring(2).split("=");
51 if (parts.length == 2) {
52 config.put(parts[0], parts[1]);
53 }
54 }
55 }
56 return config;
57 }
58
59 @Override
60 public int getPriority() {
61 return 100; // 最高优先级
62 }
63 }
64
65 // 系统属性配置源
66 public class SystemPropertyConfigSource implements ConfigSource {
67 @Override
68 public Map<String, String> loadConfig() {
69 Map<String, String> config = new HashMap<>();
70 Properties props = System.getProperties();
71 for (String key : props.stringPropertyNames()) {
72 if (key.startsWith("spring.")) {
73 config.put(key, props.getProperty(key));
74 }
75 }
76 return config;
77 }
78
79 @Override
80 public int getPriority() {
81 return 90;
82 }
83 }
84}

4.2 环境隔离

多环境配置

多环境配置示例
yaml
1# application.yml (默认配置)
2spring:
3 profiles:
4 active: dev
5 application:
6 name: user-service
7
8# application-dev.yml (开发环境)
9server:
10 port: 8081
11database:
12 url: jdbc:mysql://localhost:3306/user_dev
13 username: dev_user
14 password: dev_pass
15logging:
16 level: DEBUG
17
18# application-test.yml (测试环境)
19server:
20 port: 8082
21database:
22 url: jdbc:mysql://test-db:3306/user_test
23 username: test_user
24 password: test_pass
25logging:
26 level: INFO
27
28# application-prod.yml (生产环境)
29server:
30 port: 8083
31database:
32 url: jdbc:mysql://prod-db:3306/user_prod
33 username: prod_user
34 password: prod_pass
35logging:
36 level: WARN

5. 面试题精选

5.1 基础概念题

Q1: 什么是配置中心?它的作用是什么?

: 配置中心是微服务架构中的一个重要组件,负责管理所有服务的配置信息。

主要作用

  1. 集中管理:所有配置集中存储,便于管理
  2. 动态刷新:支持配置的动态更新,无需重启服务
  3. 环境隔离:支持多环境配置隔离
  4. 版本管理:支持配置的版本历史和回滚
  5. 安全控制:提供配置的访问控制和加密
  6. 审计追踪:记录配置的变更历史

Q2: Spring Cloud Config的工作原理是什么?

: Spring Cloud Config的工作原理如下:

  1. 启动时加载:服务启动时从Config Server拉取配置
  2. 配置存储:Config Server从Git、SVN等存储中读取配置
  3. 配置分发:Config Server将配置分发给各个客户端
  4. 动态刷新:通过/actuator/refresh端点或Spring Cloud Bus实现配置刷新
  5. 配置缓存:客户端缓存配置,减少网络请求

5.2 实践题

Q3: 如何实现配置的动态刷新?

: 配置动态刷新可以通过以下方式实现:

  1. 手动刷新
java
1@RestController
2@RefreshScope
3public class ConfigController {
4
5 @Value("${feature.enabled:false}")
6 private boolean featureEnabled;
7
8 @PostMapping("/refresh")
9 public String refresh() {
10 // 手动刷新配置
11 return "Config refreshed";
12 }
13}
  1. 自动刷新
java
1@Component
2public class ConfigChangeListener {
3
4 @EventListener
5 public void onRefreshEvent(RefreshEvent event) {
6 // 配置刷新事件处理
7 log.info("Config refreshed");
8 }
9}
  1. Spring Cloud Bus
yaml
1# 配置Bus
2spring:
3 cloud:
4 bus:
5 enabled: true
6 refresh:
7 enabled: true

Q4: 如何实现配置的加密?

: 配置加密可以通过以下方式实现:

  1. 对称加密
yaml
1# 配置加密密钥
2encrypt:
3 key: your-encryption-key
4
5# 加密配置
6database:
7 password: '{cipher}AQA...'
  1. 非对称加密
bash
1# 生成密钥对
2keytool -genkeypair -alias config-server -keyalg RSA -dname "CN=Config Server" -keypass configpass -keystore config-server.jks -storepass configpass
  1. 加密端点
java
1@RestController
2public class EncryptionController {
3
4 @PostMapping("/encrypt")
5 public String encrypt(@RequestBody String value) {
6 return textEncryptor.encrypt(value);
7 }
8
9 @PostMapping("/decrypt")
10 public String decrypt(@RequestBody String encryptedValue) {
11 return textEncryptor.decrypt(encryptedValue);
12 }
13}

5.3 架构设计题

Q5: 如何设计一个高可用的配置中心?

: 高可用配置中心设计包括以下几个方面:

  1. 多实例部署
yaml
1# Config Server集群配置
2spring:
3 cloud:
4 config:
5 server:
6 git:
7 uri: https://github.com/your-org/config-repo
8 repos:
9 user-service:
10 uri: https://github.com/your-org/user-service-config
11 order-service:
12 uri: https://github.com/your-org/order-service-config
  1. 客户端缓存
java
1@Component
2public class ConfigCache {
3
4 private final Map<String, String> configCache = new ConcurrentHashMap<>();
5
6 public String getConfig(String key) {
7 return configCache.computeIfAbsent(key, this::loadFromServer);
8 }
9
10 public void refreshCache() {
11 configCache.clear();
12 }
13}
  1. 配置备份
java
1@Component
2public class ConfigBackup {
3
4 @Scheduled(fixedRate = 3600000) // 每小时备份一次
5 public void backupConfig() {
6 // 备份配置到本地文件
7 Map<String, String> configs = configStorage.getAll();
8 writeToFile(configs, "config-backup-" + System.currentTimeMillis() + ".json");
9 }
10}

Q6: 配置中心与配置文件的区别是什么?

: 主要区别如下:

配置文件

  • 配置分散在各个服务中
  • 修改配置需要重启服务
  • 难以管理大量配置
  • 不支持动态更新
  • 配置版本管理困难

配置中心

  • 配置集中管理
  • 支持动态刷新
  • 易于管理大量配置
  • 支持配置版本管理
  • 提供配置审计功能
配置中心学习要点
  1. 理解原理:掌握配置中心的核心概念和工作原理
  2. 掌握配置:熟悉各种配置中心的配置方法
  3. 实践应用:通过实际项目练习配置中心的使用
  4. 安全控制:学会配置的加密和权限控制
  5. 监控运维:了解配置中心的监控和运维管理

通过本章的学习,你应该已经深入理解了配置中心的核心概念、实现方案和最佳实践。配置中心是微服务架构的重要基础设施,合理使用配置中心可以显著提高系统的可维护性和灵活性。在实际项目中,要根据业务需求选择合适的配置中心方案,并注重安全控制和监控运维。

参与讨论