配置中心详解
在微服务架构中,配置管理是一个重要的挑战。配置中心提供了集中化的配置管理能力,支持配置的动态刷新、多环境隔离、灰度发布等功能,是微服务架构的重要组成部分。
核心价值
配置中心 = 集中管理 + 动态刷新 + 多环境隔离 + 灰度发布 + 安全审计
配置中心架构
配置中心方案对比
- Spring Cloud Config
- Nacos
- Apollo
yaml
1# Config Server配置2spring:3 application:4 name: config-server5 cloud:6 config:7 server:8 git:9 uri: https://github.com/your-org/config-repo10 username: your-username11 password: your-password12 search-paths: config13 default-label: main14 clone-on-start: true特点:
- ✅ Spring生态集成度高
- ✅ 支持Git、SVN等多种存储
- ✅ 支持配置加密
- ❌ 功能相对简单
- ❌ 界面管理能力弱
yaml
1# Nacos配置2spring:3 cloud:4 nacos:5 config:6 server-addr: localhost:88487 file-extension: yaml8 group: DEFAULT_GROUP9 namespace: public10 refresh-enabled: true特点:
- ✅ 功能全面,支持配置和服务发现
- ✅ 界面友好,操作简单
- ✅ 支持多环境、多租户
- ✅ 中文文档丰富
- ❌ 相对较新,生态不如Spring
properties
1# Apollo配置2app.id=user-service3apollo.meta=http://localhost:80804apollo.bootstrap.enabled=true5apollo.bootstrap.namespaces=application,user-service特点:
- ✅ 功能强大,企业级特性丰富
- ✅ 支持灰度发布
- ✅ 权限管理完善
- ✅ 监控和审计功能强
- ❌ 部署复杂,学习成本高
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, DELETE81 }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 @Override41 public void onChange(String key, String oldValue, String newValue) {42 localConfig.put(key, newValue);43 notifyConfigChange(key, oldValue, newValue);44 }45 46 @Override47 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 // 解析响应JSON170 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}配置中心设计原则
- 集中管理:所有配置集中存储和管理
- 动态刷新:支持配置的动态更新和实时生效
- 环境隔离:支持多环境配置隔离
- 安全控制:提供配置的访问控制和加密
- 版本管理:支持配置的版本历史和回滚
- 性能优化:确保配置查询和更新的高性能
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 @Override44 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 @Override57 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-server4 cloud:5 config:6 server:7 git:8 uri: https://github.com/your-org/config-repo9 username: your-username10 password: your-password11 search-paths: config12 default-label: main13 clone-on-start: true14 timeout: 500015 force-pull: false16 # 支持多个Git仓库17 repos:18 user-service:19 uri: https://github.com/your-org/user-service-config20 search-paths: config21 order-service:22 uri: https://github.com/your-org/order-service-config23 search-paths: config24 # 文件系统存储25 native:26 search-locations: classpath:/config27 # 加密配置28 encrypt:29 enabled: true30 key: your-encryption-key31 # 健康检查32 health:33 repositories:34 user-service:35 label: main36 name: user-service37 profiles: dev38server:39 port: 88884041# 安全配置42security:43 basic:44 enabled: true45 user:46 name: admin47 password: admin1234849# 监控配置50management:51 endpoints:52 web:53 exposure:54 include: health,info,metrics,refresh55 endpoint:56 health:57 show-details: always启动类
Config Server启动类
java
1@EnableConfigServer2@SpringBootApplication3public class ConfigServerApplication {4 5 public static void main(String[] args) {6 SpringApplication.run(ConfigServerApplication.class, args);7 }8 9 @Bean10 public ConfigServerProperties configServerProperties() {11 ConfigServerProperties properties = new ConfigServerProperties();12 properties.setDefaultLabel("main");13 properties.setCloneOnStart(true);14 return properties;15 }16 17 @Bean18 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 @Bean26 public EncryptionController encryptionController() {27 return new EncryptionController();28 }29 30 @Bean31 public DecryptionController decryptionController() {32 return new DecryptionController();33 }34}2.3 Config Client 配置与使用
客户端配置
bootstrap.yml
yaml
1spring:2 application:3 name: user-service4 cloud:5 config:6 uri: http://localhost:88887 profile: dev8 label: main9 # 失败快速返回10 fail-fast: true11 # 重试配置12 retry:13 initial-interval: 100014 max-interval: 200015 max-attempts: 616 multiplier: 1.117 # 请求超时18 request-read-timeout: 500019 # 连接超时20 connect-timeout: 300021 # 用户名密码22 username: admin23 password: admin12324 # 配置刷新25 refresh-enabled: true26 # 配置优先级27 override-system-properties: false28 # 配置覆盖29 override-none: false30 # 配置前缀31 name: ${spring.application.name}32 # 配置后缀33 profile: ${spring.profiles.active:default}34 # 配置标签35 label: ${spring.cloud.config.label:main}客户端启动类
Config Client启动类
java
1@EnableDiscoveryClient2@SpringBootApplication3public class UserServiceApplication {4 5 public static void main(String[] args) {6 SpringApplication.run(UserServiceApplication.class, args);7 }8 9 @Bean10 @LoadBalanced11 public RestTemplate restTemplate() {12 return new RestTemplate();13 }14 15 @Bean16 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@RestController2@RefreshScope3public 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 @Autowired18 private Environment environment;19 20 @Autowired21 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}5960// 配置属性类61@ConfigurationProperties(prefix = "app")62@Data63public class AppConfigProperties {64 private String name;65 private String version;66 private DatabaseConfig database;67 private RedisConfig redis;68 private FeatureConfig feature;69 70 @Data71 public static class DatabaseConfig {72 private String url;73 private String username;74 private String password;75 private int maxConnections;76 }77 78 @Data79 public static class RedisConfig {80 private String host;81 private int port;82 private String password;83 private int database;84 }85 86 @Data87 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 @RestController58 public class EncryptionController {59 60 @Autowired61 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最佳实践
- 配置分离:将不同环境的配置分离到不同的分支或目录
- 配置加密:对敏感配置进行加密
- 配置刷新:合理使用配置刷新机制
- 监控告警:监控配置中心的健康状况
- 备份恢复:定期备份配置数据
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-service3apollo.meta=http://localhost:80804apollo.bootstrap.enabled=true5apollo.bootstrap.eagerLoad.enabled=true6apollo.bootstrap.namespaces=application,user-service7apollo.cache.enabled=true8apollo.cache.dir=/opt/data/apollo-cache9apollo.refreshInterval=300010apollo.longPollingInitialDelayInMills=100011apollo.longPollingTimeoutInSeconds=9012apollo.autoUpdateInjectedSpringProperties=true13apollo.property.order.enabled=true14apollo.property.order.enabled=true3.2 Nacos Config
Nacos是阿里巴巴开源的配置中心,支持配置的动态更新、多环境隔离、灰度发布等功能。
Nacos Config 配置
nacos-config.yml
yaml
1spring:2 application:3 name: order-service4 cloud:5 nacos:6 config:7 server-addr: localhost:88488 file-extension: yaml9 group: DEFAULT_GROUP10 namespace: public11 # 配置刷新12 refresh-enabled: true13 # 配置优先级14 shared-configs:15 - data-id: common-config.yaml16 group: DEFAULT_GROUP17 refresh: true18 - data-id: database-config.yaml19 group: DEFAULT_GROUP20 refresh: true21 # 扩展配置22 extension-configs:23 - data-id: extension-config.yaml24 group: DEFAULT_GROUP25 refresh: true26 # 配置监听27 config-long-poll-timeout: 46028 config-retry-time: 300029 max-retry: 330 enable-remote-sync-config: true4. 配置优先级与隔离
4.1 配置优先级
优先级规则
配置优先级示例
java
1public class ConfigPriority {2 /*3 * 配置优先级(从高到低)4 * 1. 命令行参数:--spring.profiles.active=prod5 * 2. 系统属性:-Dspring.profiles.active=prod6 * 3. 环境变量:SPRING_PROFILES_ACTIVE=prod7 * 4. 配置文件:application-prod.yml8 * 5. 默认配置:application.yml9 */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 @Override44 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 @Override60 public int getPriority() {61 return 100; // 最高优先级62 }63 }64 65 // 系统属性配置源66 public class SystemPropertyConfigSource implements ConfigSource {67 @Override68 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 @Override80 public int getPriority() {81 return 90;82 }83 }84}4.2 环境隔离
多环境配置
多环境配置示例
yaml
1# application.yml (默认配置)2spring:3 profiles:4 active: dev5 application:6 name: user-service78# application-dev.yml (开发环境)9server:10 port: 808111database:12 url: jdbc:mysql://localhost:3306/user_dev13 username: dev_user14 password: dev_pass15logging:16 level: DEBUG1718# application-test.yml (测试环境)19server:20 port: 808221database:22 url: jdbc:mysql://test-db:3306/user_test23 username: test_user24 password: test_pass25logging:26 level: INFO2728# application-prod.yml (生产环境)29server:30 port: 808331database:32 url: jdbc:mysql://prod-db:3306/user_prod33 username: prod_user34 password: prod_pass35logging:36 level: WARN5. 面试题精选
5.1 基础概念题
Q1: 什么是配置中心?它的作用是什么?
答: 配置中心是微服务架构中的一个重要组件,负责管理所有服务的配置信息。
主要作用:
- 集中管理:所有配置集中存储,便于管理
- 动态刷新:支持配置的动态更新,无需重启服务
- 环境隔离:支持多环境配置隔离
- 版本管理:支持配置的版本历史和回滚
- 安全控制:提供配置的访问控制和加密
- 审计追踪:记录配置的变更历史
Q2: Spring Cloud Config的工作原理是什么?
答: Spring Cloud Config的工作原理如下:
- 启动时加载:服务启动时从Config Server拉取配置
- 配置存储:Config Server从Git、SVN等存储中读取配置
- 配置分发:Config Server将配置分发给各个客户端
- 动态刷新:通过/actuator/refresh端点或Spring Cloud Bus实现配置刷新
- 配置缓存:客户端缓存配置,减少网络请求
5.2 实践题
Q3: 如何实现配置的动态刷新?
答: 配置动态刷新可以通过以下方式实现:
- 手动刷新:
java
1@RestController2@RefreshScope3public 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}- 自动刷新:
java
1@Component2public class ConfigChangeListener {3 4 @EventListener5 public void onRefreshEvent(RefreshEvent event) {6 // 配置刷新事件处理7 log.info("Config refreshed");8 }9}- Spring Cloud Bus:
yaml
1# 配置Bus2spring:3 cloud:4 bus:5 enabled: true6 refresh:7 enabled: trueQ4: 如何实现配置的加密?
答: 配置加密可以通过以下方式实现:
- 对称加密:
yaml
1# 配置加密密钥2encrypt:3 key: your-encryption-key45# 加密配置6database:7 password: '{cipher}AQA...'- 非对称加密:
bash
1# 生成密钥对2keytool -genkeypair -alias config-server -keyalg RSA -dname "CN=Config Server" -keypass configpass -keystore config-server.jks -storepass configpass- 加密端点:
java
1@RestController2public 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: 如何设计一个高可用的配置中心?
答: 高可用配置中心设计包括以下几个方面:
- 多实例部署:
yaml
1# Config Server集群配置2spring:3 cloud:4 config:5 server:6 git:7 uri: https://github.com/your-org/config-repo8 repos:9 user-service:10 uri: https://github.com/your-org/user-service-config11 order-service:12 uri: https://github.com/your-org/order-service-config- 客户端缓存:
java
1@Component2public 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}- 配置备份:
java
1@Component2public 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: 配置中心与配置文件的区别是什么?
答: 主要区别如下:
配置文件:
- 配置分散在各个服务中
- 修改配置需要重启服务
- 难以管理大量配置
- 不支持动态更新
- 配置版本管理困难
配置中心:
- 配置集中管理
- 支持动态刷新
- 易于管理大量配置
- 支持配置版本管理
- 提供配置审计功能
配置中心学习要点
- 理解原理:掌握配置中心的核心概念和工作原理
- 掌握配置:熟悉各种配置中心的配置方法
- 实践应用:通过实际项目练习配置中心的使用
- 安全控制:学会配置的加密和权限控制
- 监控运维:了解配置中心的监控和运维管理
通过本章的学习,你应该已经深入理解了配置中心的核心概念、实现方案和最佳实践。配置中心是微服务架构的重要基础设施,合理使用配置中心可以显著提高系统的可维护性和灵活性。在实际项目中,要根据业务需求选择合适的配置中心方案,并注重安全控制和监控运维。
评论