🎛️ 设计一个分布式配置中心:遥控器的魔法!

27 阅读13分钟

📖 开场:电视遥控器

想象你家有10台电视 📺:

没有配置中心(悲剧)

想调整所有电视的音量:
    ↓
电视1:手动调整 🎚️
电视2:手动调整 🎚️
电视3:手动调整 🎚️
...
电视10:手动调整 🎚️

结果:
- 累死了 😫
- 容易遗漏 ❌
- 效率低 ❌

有配置中心(爽)

万能遥控器 🎮:
    ↓
按一下按钮
    ↓
所有电视同时调整音量 🔊

结果:
- 一键搞定 ✅
- 不会遗漏 ✅
- 效率高 ✅

这就是配置中心:统一管理配置,实时推送!


🤔 为什么需要配置中心?

场景1:微服务配置管理 🎯

没有配置中心:
订单服务:application.yml
用户服务:application.yml
支付服务:application.yml
...(100个服务)

修改Redis地址:
    ↓
需要修改100个配置文件 💀
重启100个服务 💀💀

有配置中心:
配置中心:统一管理配置
    ↓
修改Redis地址
    ↓
所有服务自动刷新 ✅
不需要重启 ✅✅

场景2:灰度发布 🚦

没有配置中心:
新功能上线:
    ↓
一次性给所有用户
    ↓
有BUG → 所有用户受影响 💀

有配置中心:
新功能上线:
    ↓
先给5%用户(灰度)🚦
    ↓
没问题 → 再给100%用户 ✅
有BUG → 立即回滚 ✅

场景3:环境隔离 🏢

开发环境:dev.yml
测试环境:test.yml
生产环境:prod.yml

配置中心:
- 环境隔离 ✅
- 权限控制 ✅
- 配置版本 ✅

🎯 核心功能

功能1:配置管理 📝

配置格式:
- properties
- yaml
- json
- xml

配置组织:
- 应用(Application)
  - 环境(Environment)
    - 配置文件(Config File)

例子:
order-service(应用)
  ├── dev(开发环境)
  │   ├── application.yml
  │   └── redis.properties
  ├── test(测试环境)
  │   ├── application.yml
  │   └── redis.properties
  └── prod(生产环境)
      ├── application.yml
      └── redis.properties

功能2:实时推送 🔔

方案1:客户端定时轮询 ⏰

客户端:每5秒查询一次
    ↓
配置中心:返回配置
    ↓
客户端:对比版本号
    ↓
版本变化 → 刷新配置 ✅

缺点:
- 延迟(最多5秒)❌
- 浪费资源(频繁查询)❌

方案2:长轮询(Long Polling)⏳⭐

客户端:请求配置
    ↓
配置中心:配置没变化,Hold住请求(不返回)
    ↓
30秒后:
- 配置变化 → 立即返回 ✅
- 超时 → 返回304(Not Modified)
    ↓
客户端:收到响应后,立即发起下一次请求

优点:
- 实时性高(配置变化立即推送)✅
- 节省资源(不频繁查询)✅

代码实现(服务端):

@RestController
@RequestMapping("/config")
public class ConfigController {
    
    private final ConfigService configService;
    private final DeferredResultManager deferredResultManager;
    
    /**
     * ⭐ 长轮询获取配置
     */
    @GetMapping("/get")
    public DeferredResult<ResponseEntity<String>> getConfig(
            @RequestParam String appId,
            @RequestParam String env,
            @RequestParam Long version
    ) {
        // ⭐ 创建DeferredResult(超时30秒)
        DeferredResult<ResponseEntity<String>> result = 
            new DeferredResult<>(30000L);
        
        // 超时处理
        result.onTimeout(() -> {
            result.setResult(ResponseEntity.status(HttpStatus.NOT_MODIFIED).build());
        });
        
        // ⭐ 检查配置版本
        Long currentVersion = configService.getVersion(appId, env);
        
        if (!currentVersion.equals(version)) {
            // 配置有更新,立即返回
            String config = configService.getConfig(appId, env);
            result.setResult(ResponseEntity.ok(config));
        } else {
            // 配置没更新,Hold住请求
            deferredResultManager.addDeferredResult(appId, env, result);
        }
        
        return result;
    }
    
    /**
     * ⭐ 配置变化时,触发长轮询返回
     */
    @PostMapping("/update")
    public void updateConfig(
            @RequestParam String appId,
            @RequestParam String env,
            @RequestBody String config
    ) {
        // 更新配置
        configService.updateConfig(appId, env, config);
        
        // ⭐ 触发所有等待的长轮询请求
        String newConfig = configService.getConfig(appId, env);
        deferredResultManager.notifyConfigChange(appId, env, newConfig);
    }
}

/**
 * DeferredResult管理器
 */
@Component
public class DeferredResultManager {
    
    // key: appId:env, value: 等待的DeferredResult列表
    private Map<String, List<DeferredResult<ResponseEntity<String>>>> 
        deferredResultMap = new ConcurrentHashMap<>();
    
    /**
     * 添加DeferredResult
     */
    public void addDeferredResult(String appId, String env, 
                                   DeferredResult<ResponseEntity<String>> result) {
        String key = appId + ":" + env;
        deferredResultMap.computeIfAbsent(key, k -> new CopyOnWriteArrayList<>())
                         .add(result);
        
        // 完成后移除
        result.onCompletion(() -> {
            deferredResultMap.get(key).remove(result);
        });
    }
    
    /**
     * ⭐ 通知配置变化
     */
    public void notifyConfigChange(String appId, String env, String config) {
        String key = appId + ":" + env;
        List<DeferredResult<ResponseEntity<String>>> results = 
            deferredResultMap.get(key);
        
        if (results != null) {
            // 触发所有等待的请求
            results.forEach(result -> {
                result.setResult(ResponseEntity.ok(config));
            });
            results.clear();
        }
    }
}

客户端

@Component
public class ConfigClient {
    
    @Autowired
    private RestTemplate restTemplate;
    
    private String appId = "order-service";
    private String env = "prod";
    private Long version = 0L;
    private String config;
    
    @PostConstruct
    public void startLongPolling() {
        // ⭐ 启动长轮询
        new Thread(() -> {
            while (true) {
                try {
                    longPolling();
                } catch (Exception e) {
                    e.printStackTrace();
                    // 出错后等待5秒再重试
                    Thread.sleep(5000);
                }
            }
        }).start();
    }
    
    /**
     * ⭐ 长轮询
     */
    private void longPolling() throws Exception {
        String url = "http://config-server/config/get" +
                     "?appId=" + appId +
                     "&env=" + env +
                     "&version=" + version;
        
        ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
        
        if (response.getStatusCode() == HttpStatus.OK) {
            // 配置有更新
            String newConfig = response.getBody();
            
            if (!newConfig.equals(config)) {
                // ⭐ 刷新配置
                config = newConfig;
                version++;
                onConfigChange(newConfig);
            }
        }
        
        // 立即发起下一次长轮询
    }
    
    /**
     * 配置变化回调
     */
    private void onConfigChange(String newConfig) {
        System.out.println("⭐ 配置更新了:" + newConfig);
        // 刷新Spring Bean的配置
        // ...
    }
}

方案3:WebSocket 🔌

客户端 ←→ 配置中心:建立WebSocket连接
    ↓
配置变化:
    ↓
配置中心:主动推送 → 客户端

优点:
- 实时性最高 ✅
- 双向通信 ✅

缺点:
- 连接数多(每个服务都要连接)❌
- 复杂度高 ❌

功能3:配置版本管理 📚

配置历史:
v1: redis.host = 192.168.1.1
v2: redis.host = 192.168.1.2
v3: redis.host = 192.168.1.3

功能:
- 版本记录 ✅
- 版本回滚 ✅
- 版本对比 ✅

数据库设计

-- ⭐ 配置表
CREATE TABLE config (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    app_id VARCHAR(100) NOT NULL COMMENT '应用ID',
    env VARCHAR(50) NOT NULL COMMENT '环境',
    config_key VARCHAR(200) NOT NULL COMMENT '配置key',
    config_value TEXT COMMENT '配置value',
    version BIGINT NOT NULL DEFAULT 0 COMMENT '版本号',
    created_by VARCHAR(100) COMMENT '创建人',
    created_time DATETIME COMMENT '创建时间',
    updated_by VARCHAR(100) COMMENT '更新人',
    updated_time DATETIME COMMENT '更新时间',
    UNIQUE KEY uk_app_env_key (app_id, env, config_key)
) COMMENT '配置表';

-- ⭐ 配置历史表
CREATE TABLE config_history (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    config_id BIGINT NOT NULL COMMENT '配置ID',
    app_id VARCHAR(100) NOT NULL COMMENT '应用ID',
    env VARCHAR(50) NOT NULL COMMENT '环境',
    config_key VARCHAR(200) NOT NULL COMMENT '配置key',
    config_value TEXT COMMENT '配置value',
    version BIGINT NOT NULL COMMENT '版本号',
    operation VARCHAR(50) COMMENT '操作类型:CREATE/UPDATE/DELETE',
    operator VARCHAR(100) COMMENT '操作人',
    operation_time DATETIME COMMENT '操作时间',
    KEY idx_config_id (config_id)
) COMMENT '配置历史表';

版本回滚

@Service
public class ConfigService {
    
    @Autowired
    private ConfigMapper configMapper;
    
    @Autowired
    private ConfigHistoryMapper configHistoryMapper;
    
    /**
     * ⭐ 更新配置(带版本控制)
     */
    @Transactional
    public void updateConfig(String appId, String env, String key, String value) {
        // 查询当前配置
        Config config = configMapper.selectByAppIdAndEnvAndKey(appId, env, key);
        
        if (config == null) {
            // 新增配置
            config = new Config();
            config.setAppId(appId);
            config.setEnv(env);
            config.setConfigKey(key);
            config.setConfigValue(value);
            config.setVersion(1L);
            configMapper.insert(config);
            
            // 记录历史
            saveHistory(config, "CREATE");
        } else {
            // ⭐ 更新配置(版本号+1)
            String oldValue = config.getConfigValue();
            config.setConfigValue(value);
            config.setVersion(config.getVersion() + 1);
            configMapper.updateById(config);
            
            // 记录历史
            saveHistory(config, "UPDATE");
        }
        
        // ⭐ 触发配置变化通知
        notifyConfigChange(appId, env);
    }
    
    /**
     * ⭐ 回滚到指定版本
     */
    @Transactional
    public void rollback(Long configId, Long targetVersion) {
        // 查询目标版本的配置
        ConfigHistory history = configHistoryMapper.selectByConfigIdAndVersion(
            configId, targetVersion);
        
        if (history == null) {
            throw new RuntimeException("目标版本不存在");
        }
        
        // 查询当前配置
        Config config = configMapper.selectById(configId);
        
        // ⭐ 回滚配置值
        config.setConfigValue(history.getConfigValue());
        config.setVersion(config.getVersion() + 1);
        configMapper.updateById(config);
        
        // 记录历史
        saveHistory(config, "ROLLBACK");
        
        // 触发配置变化通知
        notifyConfigChange(config.getAppId(), config.getEnv());
    }
    
    /**
     * 保存配置历史
     */
    private void saveHistory(Config config, String operation) {
        ConfigHistory history = new ConfigHistory();
        history.setConfigId(config.getId());
        history.setAppId(config.getAppId());
        history.setEnv(config.getEnv());
        history.setConfigKey(config.getConfigKey());
        history.setConfigValue(config.getConfigValue());
        history.setVersion(config.getVersion());
        history.setOperation(operation);
        history.setOperationTime(new Date());
        configHistoryMapper.insert(history);
    }
}

功能4:灰度发布 🚦

灰度发布:
1. 先给5%用户推送新配置
2. 观察5分钟,没问题
3. 再给50%用户推送
4. 再观察5分钟,没问题
5. 最后给100%用户推送

好处:
- 降低风险 ✅
- 快速回滚 ✅

实现

@Service
public class GrayReleaseService {
    
    @Autowired
    private ConfigService configService;
    
    /**
     * ⭐ 灰度发布
     */
    public void grayRelease(String appId, String env, String key, String value, 
                            int grayPercent) {
        // 创建灰度规则
        GrayRule rule = new GrayRule();
        rule.setAppId(appId);
        rule.setEnv(env);
        rule.setConfigKey(key);
        rule.setGrayValue(value);
        rule.setGrayPercent(grayPercent);
        
        // 保存灰度规则
        grayRuleMapper.insert(rule);
        
        // 触发配置变化通知(只通知灰度用户)
        notifyGrayConfigChange(appId, env, rule);
    }
    
    /**
     * ⭐ 获取配置(灰度逻辑)
     */
    public String getConfig(String appId, String env, String key, String clientId) {
        // 查询灰度规则
        GrayRule rule = grayRuleMapper.selectByAppIdAndEnvAndKey(appId, env, key);
        
        if (rule != null) {
            // ⭐ 判断是否命中灰度
            if (isGrayClient(clientId, rule.getGrayPercent())) {
                // 命中灰度,返回灰度值
                return rule.getGrayValue();
            }
        }
        
        // 返回正常值
        Config config = configService.getConfig(appId, env, key);
        return config.getConfigValue();
    }
    
    /**
     * 判断是否命中灰度
     */
    private boolean isGrayClient(String clientId, int grayPercent) {
        // ⭐ 使用一致性哈希
        int hash = Math.abs(clientId.hashCode());
        int mod = hash % 100;
        return mod < grayPercent;
    }
}

功能5:权限控制 🔒

角色权限:
- 开发人员:只能查看和修改开发环境
- 测试人员:只能查看和修改测试环境
- 运维人员:可以查看和修改所有环境

操作审计:
- 谁修改了配置
- 什么时候修改的
- 修改了什么内容

Spring Security配置

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            // ⭐ 只有ADMIN角色可以修改生产环境配置
            .antMatchers("/config/prod/**").hasRole("ADMIN")
            // 开发人员可以修改开发环境配置
            .antMatchers("/config/dev/**").hasAnyRole("DEVELOPER", "ADMIN")
            // 测试人员可以修改测试环境配置
            .antMatchers("/config/test/**").hasAnyRole("TESTER", "ADMIN")
            .anyRequest().authenticated()
            .and()
            .formLogin();
    }
}

🏗️ 架构设计

整体架构

        分布式配置中心架构

┌─────────────────────────────────────┐
│          管理控制台 🖥️              │
│  - 配置管理                         │
│  - 版本管理                         │
│  - 灰度发布                         │
│  - 权限控制                         │
└──────────────┬──────────────────────┘
               │
               ↓
┌─────────────────────────────────────┐
│        配置中心服务器(集群)        │
│                                     │
│  ┌────────────┐  ┌────────────┐    │
│  │  Server1   │  │  Server2   │    │
│  └────────────┘  └────────────┘    │
│                                     │
│  - 长轮询                           │
│  - 配置推送                         │
│  - 版本管理                         │
│  - 灰度发布                         │
└──────────────┬──────────────────────┘
               │
               ↓
┌──────────────────────────────────────┐
│            数据库                     │
│  - MySQL(配置数据)                 │
│  - Redis(配置缓存)                 │
└──────────────────────────────────────┘
               ↑
               │ 长轮询
               │
┌──────────────┴──────────────────────┐
│         应用服务(客户端)           │
│                                     │
│  ┌────────────┐  ┌────────────┐    │
│  │ 订单服务   │  │ 用户服务   │    │
│  └────────────┘  └────────────┘    │
└─────────────────────────────────────┘

高可用设计

1. 配置中心集群 🏢

配置中心:多节点部署
    ↓
Nginx负载均衡
    ↓
客户端:随机连接一个节点

节点宕机:
    ↓
客户端:自动切换到其他节点 ✅

2. 配置缓存 💾

配置获取流程:
1. Redis缓存:查询配置
2. 缓存命中 → 返回 ✅
3. 缓存未命中 → 查询MySQL
4. 写入Redis缓存
5. 返回配置

好处:
- 减轻数据库压力 ✅
- 提高响应速度 ✅

3. 本地缓存 📁

客户端:
1. 启动时,从配置中心获取配置
2. 保存到本地文件:/config/app.yml
3. 加载本地配置文件

配置中心宕机:
    ↓
客户端:使用本地缓存的配置 ✅
    ↓
服务不受影响 ✅

代码实现

@Component
public class ConfigClient {
    
    private String localConfigFile = "/config/app.yml";
    
    /**
     * ⭐ 加载配置
     */
    public Properties loadConfig() {
        try {
            // 1. 尝试从配置中心获取
            Properties config = fetchFromConfigServer();
            
            // 2. 保存到本地文件
            saveToLocal(config);
            
            return config;
            
        } catch (Exception e) {
            // 配置中心不可用,使用本地缓存
            return loadFromLocal();
        }
    }
    
    /**
     * 从配置中心获取配置
     */
    private Properties fetchFromConfigServer() {
        // HTTP请求配置中心
        // ...
    }
    
    /**
     * ⭐ 保存配置到本地
     */
    private void saveToLocal(Properties config) throws IOException {
        try (FileOutputStream fos = new FileOutputStream(localConfigFile)) {
            config.store(fos, "Config from config server");
        }
    }
    
    /**
     * ⭐ 从本地文件加载配置
     */
    private Properties loadFromLocal() {
        Properties config = new Properties();
        try (FileInputStream fis = new FileInputStream(localConfigFile)) {
            config.load(fis);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return config;
    }
}

🎓 开源方案

Apollo(携程开源)⭐⭐⭐

特点

  • 功能强大 ✅
  • 可视化界面 ✅
  • 灰度发布 ✅
  • 权限控制 ✅
  • 长轮询推送 ✅

使用

  1. 引入依赖
<dependency>
    <groupId>com.ctrip.framework.apollo</groupId>
    <artifactId>apollo-client</artifactId>
    <version>1.9.0</version>
</dependency>
  1. 配置
# apollo配置
app.id=order-service
apollo.meta=http://config-server:8080
apollo.bootstrap.enabled=true
apollo.bootstrap.namespaces=application
  1. 使用
@Configuration
public class AppConfig {
    
    /**
     * ⭐ 自动刷新配置
     */
    @Value("${redis.host}")
    private String redisHost;
    
    @Value("${redis.port}")
    private int redisPort;
    
    /**
     * ⭐ 监听配置变化
     */
    @ApolloConfigChangeListener
    public void onChange(ConfigChangeEvent changeEvent) {
        for (String key : changeEvent.changedKeys()) {
            ConfigChange change = changeEvent.getChange(key);
            System.out.println("配置变化:" + key + 
                             " 从 " + change.getOldValue() + 
                             " 改为 " + change.getNewValue());
        }
    }
}

Nacos(阿里开源)⭐⭐⭐

特点

  • 配置中心 + 注册中心 ✅
  • 简单易用 ✅
  • 支持多种配置格式 ✅

使用

  1. 引入依赖
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
  1. 配置
spring:
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848  # Nacos服务器地址
        namespace: dev                # 命名空间(环境隔离)
        group: DEFAULT_GROUP          # 分组
        file-extension: yaml          # 配置文件格式
  1. 使用
@RestController
@RefreshScope  // ⭐ 自动刷新配置
public class ConfigController {
    
    @Value("${redis.host}")
    private String redisHost;
    
    @GetMapping("/config")
    public String getConfig() {
        return "Redis Host: " + redisHost;
    }
}

Spring Cloud Config ⭐⭐

特点

  • Spring生态 ✅
  • 基于Git ✅
  • 简单 ✅

架构

Git仓库(配置文件)
    ↓
Config Server(配置中心)
    ↓
Config Client(应用服务)

使用

  1. Config Server
spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/your-repo/config-repo
          username: your-username
          password: your-password
  1. Config Client
spring:
  cloud:
    config:
      uri: http://config-server:8888
      name: order-service
      profile: prod

📊 对比总结

特性ApolloNacosSpring Cloud Config
功能⭐⭐⭐⭐⭐⭐⭐⭐
易用性⭐⭐⭐⭐⭐⭐⭐⭐
灰度发布
权限控制
实时推送长轮询长轮询需要Bus
可视化界面
社区活跃度⭐⭐⭐⭐⭐⭐⭐⭐

推荐

  • 大型项目:Apollo(功能强大)
  • 中小型项目:Nacos(简单易用)
  • Spring项目:Spring Cloud Config(生态好)

🎓 面试题速答

Q1: 配置中心有哪些核心功能?

A: 五大功能

  1. 配置管理

    • 集中管理配置
    • 环境隔离(dev/test/prod)
  2. 实时推送

    • 长轮询
    • WebSocket
    • 配置变化立即生效
  3. 版本管理

    • 版本记录
    • 版本回滚
    • 版本对比
  4. 灰度发布

    • 先给5%用户推送
    • 逐步扩大范围
    • 降低风险
  5. 权限控制

    • 角色权限
    • 操作审计

Q2: 如何实现配置的实时推送?

A: **长轮询(推荐)**⭐:

// 服务端
@GetMapping("/config/get")
public DeferredResult<String> getConfig(
        @RequestParam Long version) {
    
    DeferredResult<String> result = new DeferredResult<>(30000L);
    
    Long currentVersion = configService.getVersion();
    
    if (!currentVersion.equals(version)) {
        // 配置有更新,立即返回
        result.setResult(configService.getConfig());
    } else {
        // 配置没更新,Hold住请求
        deferredResultManager.add(result);
    }
    
    return result;
}

// 配置更新时触发
public void updateConfig(String config) {
    configService.updateConfig(config);
    deferredResultManager.notifyAll(config);  // 触发所有等待的请求
}

优点

  • 实时性高
  • 节省资源

Q3: 灰度发布如何实现?

A: 一致性哈希

public String getConfig(String key, String clientId) {
    // 查询灰度规则
    GrayRule rule = grayRuleMapper.selectByKey(key);
    
    if (rule != null) {
        // ⭐ 使用clientId的hash值判断是否命中灰度
        int hash = Math.abs(clientId.hashCode());
        int mod = hash % 100;
        
        if (mod < rule.getGrayPercent()) {
            // 命中灰度,返回灰度值
            return rule.getGrayValue();
        }
    }
    
    // 返回正常值
    return configService.getConfig(key);
}

流程

  1. 先给5%用户推送新配置
  2. 观察5分钟,没问题
  3. 再给50%用户推送
  4. 最后给100%用户推送

Q4: 配置中心如何保证高可用?

A: 三层保障

  1. 配置中心集群

    • 多节点部署
    • Nginx负载均衡
    • 节点宕机自动切换
  2. 配置缓存

    • Redis缓存配置
    • 减轻数据库压力
  3. 本地缓存

    • 客户端保存本地配置文件
    • 配置中心宕机时使用本地缓存
    • 服务不受影响 ✅

Q5: Apollo和Nacos如何选择?

A: 根据项目规模

Apollo

  • 大型项目 ✅
  • 功能强大(灰度发布、权限控制)
  • 可视化界面好

Nacos

  • 中小型项目 ✅
  • 简单易用
  • 配置中心 + 注册中心二合一
  • 阿里生态

推荐

  • 没特殊需求 → Nacos
  • 需要复杂灰度发布 → Apollo

🎬 总结

       配置中心核心功能

┌────────────────────────────────────┐
│ 1. 配置管理                        │
│    - 集中管理                      │
│    - 环境隔离                      │
└────────────────────────────────────┘

┌────────────────────────────────────┐
│ 2. 实时推送(长轮询)⭐             │
│    - 配置变化立即推送              │
│    - 实时性高                      │
└────────────────────────────────────┘

┌────────────────────────────────────┐
│ 3. 版本管理                        │
│    - 版本记录                      │
│    - 版本回滚                      │
└────────────────────────────────────┘

┌────────────────────────────────────┐
│ 4. 灰度发布                        │
│    - 降低风险                      │
│    - 逐步推送                      │
└────────────────────────────────────┘

┌────────────────────────────────────┐
│ 5. 权限控制                        │
│    - 角色权限                      │
│    - 操作审计                      │
└────────────────────────────────────┘

🎉 恭喜你!

你已经完全掌握了分布式配置中心的设计!🎊

核心要点

  1. 长轮询:实时推送配置,实时性高
  2. 版本管理:版本记录和回滚
  3. 灰度发布:降低风险
  4. 本地缓存:配置中心宕机时使用本地缓存

下次面试,这样回答

"配置中心的核心功能是集中管理配置、实时推送、版本管理、灰度发布和权限控制。

实时推送使用长轮询实现。客户端请求配置时,如果配置没变化,服务端Hold住请求不返回。配置变化时立即返回,客户端收到后刷新配置。这种方式实时性高且节省资源。

版本管理通过配置历史表记录每次配置变更,包括版本号、操作人、操作时间等。需要回滚时,从历史表查询目标版本的配置值,更新到当前配置表,并触发推送。

灰度发布使用一致性哈希实现。根据客户端ID的hash值判断是否命中灰度,先给5%用户推送新配置,观察无误后逐步扩大到100%,降低风险。

高可用方面,配置中心采用集群部署,客户端通过Nginx负载均衡连接。配置使用Redis缓存,客户端启动时将配置保存到本地文件。配置中心宕机时,客户端使用本地缓存,服务不受影响。

我们项目使用Apollo作为配置中心,通过@ApolloConfigChangeListener注解监听配置变化,结合@RefreshScope实现配置的自动刷新。生产环境配置修改需要灰度发布,先给10%机器推送,观察10分钟无误后再全量推送。"

面试官:👍 "很好!你对配置中心的设计理解很深刻!"


本文完 🎬

上一篇: 206-设计一个限流系统.md
下一篇: 208-设计一个电商系统的订单服务.md

作者注:写完这篇,我觉得自己可以做运维了!🎛️
如果这篇文章对你有帮助,请给我一个Star⭐!