从零实现一个分布式配置中心:Apollo的核心设计

从零实现一个分布式配置中心:Apollo的核心设计 前言你有没有想过在微服务架构中几十个服务的配置是怎么集中管理的修改一个配置项怎么让所有服务实时生效配置中心是微服务架构实现配置集中化、动态化的核心组件。今天我们用C语言从零实现一个配置中心的核心功能· 配置存储多环境/多版本· 配置推送长轮询· 配置监听与热更新· 配置回滚· 灰度发布· 配置审计· 客户端SDK---一、配置中心核心原理1. 架构图┌─────────────────────────────────────────────────────────────┐│ 配置管理平台 ││ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ││ │ 配置存储 │ │ 配置推送 │ │ 配置审计 │ ││ │ (DB) │ │ (长轮询) │ │ (日志) │ ││ └─────────────┘ └─────────────┘ └─────────────┘ ││ │ │ │ ││ ▼ ▼ ▼ ││ ┌─────────────────────────────────────────────────────┐ ││ │ 配置缓存 版本管理 │ ││ └─────────────────────────────────────────────────────┘ │└─────────────────────────────────────────────────────────────┘│ │ │▼ ▼ ▼┌─────────┐ ┌─────────┐ ┌─────────┐│ 服务A │ │ 服务B │ │ 服务C ││ 拉取配置 │ │ 监听配置 │ │ 热更新 │└─────────┘ └─────────┘ └─────────┘2. 核心概念概念 说明AppId 应用标识Env 环境dev/test/prodCluster 集群默认defaultNamespace 配置文件application/mysql/redisKey/Value 配置项Release 配置发布版本Watch 配置监听---二、完整代码实现1. 基础数据结构c#include stdio.h#include stdlib.h#include string.h#include unistd.h#include pthread.h#include time.h#include errno.h#include sys/socket.h#include netinet/in.h#include arpa/inet.h#include sys/stat.h#define MAX_CONFIG_KEY 256#define MAX_CONFIG_VALUE 4096#define MAX_APP_ID 64#define MAX_ENV 16#define MAX_CLUSTER 32#define MAX_NAMESPACE 64#define MAX_OPERATOR 64#define MAX_WATCHERS 1000// 配置条目typedef struct config_item {char key[MAX_CONFIG_KEY];char value[MAX_CONFIG_VALUE];char app_id[MAX_APP_ID];char env[MAX_ENV];char cluster[MAX_CLUSTER];char namespace_name[MAX_NAMESPACE];long long version;time_t create_time;time_t update_time;char created_by[MAX_OPERATOR];char updated_by[MAX_OPERATOR];struct config_item *next;} config_item_t;// 配置版本历史typedef struct config_history {char key[MAX_CONFIG_KEY];char value[MAX_CONFIG_VALUE];long long version;time_t update_time;char updated_by[MAX_OPERATOR];char change_type[32];struct config_history *next;} config_history_t;// 配置监听器typedef struct config_watcher {char app_id[MAX_APP_ID];char env[MAX_ENV];char namespace_name[MAX_NAMESPACE];char client_id[64];long long last_version;int notify_fd;struct config_watcher *next;} config_watcher_t;// 配置中心typedef struct config_center {config_item_t *configs;config_history_t *history;config_watcher_t *watchers;long long version_counter;int port;int running;pthread_mutex_t mutex;pthread_t notify_thread;} config_center_t;// 配置客户端typedef struct config_client {char app_id[MAX_APP_ID];char env[MAX_ENV];char cluster[MAX_CLUSTER];char server_host[64];int server_port;int sock_fd;pthread_t watch_thread;int running;void (*on_change)(const char *key, const char *value);} config_client_t;2. 配置存储c// 创建配置中心config_center_t *config_center_create(int port) {config_center_t *cc malloc(sizeof(config_center_t));memset(cc, 0, sizeof(config_center_t));cc-port port;cc-running 1;cc-version_counter 0;pthread_mutex_init(cc-mutex, NULL);printf([配置中心] 启动端口: %d\n, port);return cc;}// 获取配置char *config_get(config_center_t *cc, const char *app_id, const char *env,const char *cluster, const char *namespace_name,const char *key, long long *version) {pthread_mutex_lock(cc-mutex);config_item_t *item cc-configs;while (item) {if (strcmp(item-app_id, app_id) 0 strcmp(item-env, env) 0 strcmp(item-cluster, cluster) 0 strcmp(item-namespace_name, namespace_name) 0 strcmp(item-key, key) 0) {if (version) *version item-version;char *value strdup(item-value);pthread_mutex_unlock(cc-mutex);return value;}item item-next;}pthread_mutex_unlock(cc-mutex);return NULL;}// 获取全部配置config_item_t **config_get_all(config_center_t *cc, const char *app_id,const char *env, const char *cluster,const char *namespace_name, int *count) {pthread_mutex_lock(cc-mutex);config_item_t **items malloc(sizeof(config_item_t*) * 100);int idx 0;config_item_t *item cc-configs;while (item idx 100) {if (strcmp(item-app_id, app_id) 0 strcmp(item-env, env) 0 strcmp(item-cluster, cluster) 0 strcmp(item-namespace_name, namespace_name) 0) {items[idx] item;}item item-next;}*count idx;pthread_mutex_unlock(cc-mutex);return items;}// 设置配置int config_set(config_center_t *cc, const char *app_id, const char *env,const char *cluster, const char *namespace_name,const char *key, const char *value, const char *operator_name) {pthread_mutex_lock(cc-mutex);config_item_t *item cc-configs;config_item_t *prev NULL;while (item) {if (strcmp(item-app_id, app_id) 0 strcmp(item-env, env) 0 strcmp(item-cluster, cluster) 0 strcmp(item-namespace_name, namespace_name) 0 strcmp(item-key, key) 0) {// 保存历史config_history_t *h malloc(sizeof(config_history_t));strcpy(h-key, key);strcpy(h-value, item-value);h-version item-version;h-update_time time(NULL);strcpy(h-updated_by, operator_name);strcpy(h-change_type, UPDATE);h-next cc-history;cc-history h;// 更新strcpy(item-value, value);item-version;item-update_time time(NULL);strcpy(item-updated_by, operator_name);break;}prev item;item item-next;}if (!item) {// 新建item malloc(sizeof(config_item_t));strcpy(item-app_id, app_id);strcpy(item-env, env);strcpy(item-cluster, cluster);strcpy(item-namespace_name, namespace_name);strcpy(item-key, key);strcpy(item-value, value);item-version 1;item-create_time time(NULL);item-update_time time(NULL);strcpy(item-created_by, operator_name);strcpy(item-updated_by, operator_name);item-next cc-configs;cc-configs item;}long long new_version item-version;cc-version_counter;pthread_mutex_unlock(cc-mutex);// 通知监听器config_notify_watchers(cc, app_id, env, namespace_name, new_version);printf([配置] %s.%s.%s.%s %s (v%lld)\n,app_id, env, namespace_name, key, value, new_version);return 0;}3. 配置推送长轮询c// 添加监听器void config_add_watcher(config_center_t *cc, const char *app_id,const char *env, const char *namespace_name,const char *client_id, int notify_fd,long long last_version) {pthread_mutex_lock(cc-mutex);config_watcher_t *w malloc(sizeof(config_watcher_t));strcpy(w-app_id, app_id);strcpy(w-env, env);strcpy(w-namespace_name, namespace_name);strcpy(w-client_id, client_id);w-notify_fd notify_fd;w-last_version last_version;w-next cc-watchers;cc-watchers w;pthread_mutex_unlock(cc-mutex);}// 通知监听器void config_notify_watchers(config_center_t *cc, const char *app_id,const char *env, const char *namespace_name,long long new_version) {pthread_mutex_lock(cc-mutex);config_watcher_t *w cc-watchers;config_watcher_t *prev NULL;while (w) {if (strcmp(w-app_id, app_id) 0 strcmp(w-env, env) 0 strcmp(w-namespace_name, namespace_name) 0 w-last_version new_version) {char notify_msg[512];snprintf(notify_msg, sizeof(notify_msg),CONFIG_CHANGE:%s:%s:%s:%lld\n,app_id, env, namespace_name, new_version);send(w-notify_fd, notify_msg, strlen(notify_msg), 0);if (prev) prev-next w-next;else cc-watchers w-next;config_watcher_t *to_free w;w w-next;free(to_free);continue;}prev w;w w-next;}pthread_mutex_unlock(cc-mutex);}4. 配置回滚c// 回滚到指定版本int config_rollback(config_center_t *cc, const char *app_id, const char *env,const char *cluster, const char *namespace_name,const char *key, long long target_version,const char *operator_name) {pthread_mutex_lock(cc-mutex);// 查找目标版本config_history_t *h cc-history;while (h) {if (strcmp(h-key, key) 0 h-version target_version) {// 回滚config_set(cc, app_id, env, cluster, namespace_name,key, h-value, operator_name);printf([回滚] %s.%s.%s.%s 回滚到 v%lld\n,app_id, env, namespace_name, key, target_version);pthread_mutex_unlock(cc-mutex);return 0;}h h-next;}pthread_mutex_unlock(cc-mutex);return -1;}5. 客户端SDKc// 创建配置客户端config_client_t *config_client_create(const char *app_id, const char *env,const char *server_host, int server_port) {config_client_t *c malloc(sizeof(config_client_t));strcpy(c-app_id, app_id);strcpy(c-env, env);strcpy(c-cluster, default);strcpy(c-server_host, server_host);c-server_port server_port;c-running 1;c-on_change NULL;// 连接配置中心c-sock_fd socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in addr;addr.sin_family AF_INET;addr.sin_port htons(server_port);inet_pton(AF_INET, server_host, addr.sin_addr);connect(c-sock_fd, (struct sockaddr*)addr, sizeof(addr));printf([客户端] %s.%s 已连接配置中心\n, app_id, env);return c;}// 获取配置char *config_client_get(config_client_t *c, const char *namespace_name,const char *key) {char request[512];snprintf(request, sizeof(request), GET:%s:%s:%s:%s\n,c-app_id, c-env, namespace_name, key);send(c-sock_fd, request, strlen(request), 0);char response[MAX_CONFIG_VALUE];int n recv(c-sock_fd, response, sizeof(response) - 1, 0);if (n 0) {response[n] \0;return strdup(response);}return NULL;}// 监听配置void config_client_watch(config_client_t *c, const char *namespace_name) {char request[512];snprintf(request, sizeof(request), WATCH:%s:%s:%s:%lld\n,c-app_id, c-env, namespace_name, 0LL);send(c-sock_fd, request, strlen(request), 0);}// 监听线程void *config_watch_thread(void *arg) {config_client_t *c (config_client_t*)arg;char buffer[512];while (c-running) {int n recv(c-sock_fd, buffer, sizeof(buffer) - 1, 0);if (n 0) break;buffer[n] \0;// CONFIG_CHANGE:app_id:env:namespace:versionif (strncmp(buffer, CONFIG_CHANGE:, 14) 0) {char app_id[64], env[16], namespace_name[64];long long version;sscanf(buffer 14, %[^:]:%[^:]:%[^:]:%lld,app_id, env, namespace_name, version);// 拉取新配置char **configs config_client_pull(c, namespace_name);if (c-on_change configs) {// 回调应用c-on_change(namespace_name, (const char**)configs);}}}return NULL;}void config_client_set_callback(config_client_t *c,void (*callback)(const char*, const char**)) {c-on_change callback;pthread_create(c-watch_thread, NULL, config_watch_thread, c);}6. 测试代码cvoid test_config_center() {printf( 配置中心测试 \n\n);config_center_t *cc config_center_create(8888);// 设置配置config_set(cc, order-service, dev, default, application,db.url, jdbc:mysql://localhost:3306/test, admin);config_set(cc, order-service, dev, default, application,cache.ttl, 3600, admin);config_set(cc, order-service, prod, default, application,db.url, jdbc:mysql://prod:3306/test, admin);// 获取配置long long version;char *db_url config_get(cc, order-service, dev, default,application, db.url, version);printf(dev.db.url %s (v%lld)\n, db_url, version);free(db_url);// 获取全部配置int count;config_item_t **items config_get_all(cc, order-service, dev,default, application, count);printf(\ndev环境配置:\n);for (int i 0; i count; i) {printf( %s %s\n, items[i]-key, items[i]-value);}free(items);// 更新配置config_set(cc, order-service, dev, default, application,cache.ttl, 7200, admin);// 回滚config_rollback(cc, order-service, dev, default, application,cache.ttl, 1, admin);// 获取更新后的值char *cache_ttl config_get(cc, order-service, dev, default,application, cache.ttl, version);printf(\ncache.ttl %s (v%lld)\n, cache_ttl, version);free(cache_ttl);printf(\n配置中心运行在端口: %d\n, cc-port);free(cc);}int main() {test_config_center();return 0;}---三、编译和运行bashgcc -o config_center config_center.c -lpthread./config_center---四、Apollo vs 本实现特性 本实现 Apollo多环境 ✅ ✅多版本 ✅ ✅配置回滚 ✅ ✅实时推送 ✅ 长轮询 ✅ 长轮询灰度发布 ❌ ✅权限控制 ❌ ✅配置审计 ✅ 基础 ✅ 完整可视化界面 ❌ ✅---五、总结通过这篇文章你学会了· 配置中心的核心功能存储、推送、回滚· 配置版本管理· 长轮询推送机制· 配置回滚实现· 客户端SDK设计配置中心是微服务架构的基础组件。掌握它你就理解了Apollo、Nacos Config的底层设计。下一篇预告《从零实现一个分布式任务调度器XXL-JOB的核心设计》---评论区分享一下你用配置中心解决过什么场景