前言
你有没有想过:微服务架构中,几十上百个服务怎么统一管理配置?改一个数据库密码,难道要重启所有服务?
配置中心解决了这个问题:配置集中管理、动态刷新、服务实时感知。
今天我们用C语言从零实现一个分布式配置中心:
· 配置存储(KV)
· 服务注册与发现
· 配置热更新(长轮询/推送)
· 多环境隔离(dev/prod)
· 版本管理(回滚支持)
· 完整的客户端SDK
一、配置中心核心原理
- 架构图
```
┌─────────┐ ┌─────────────────────────────────────┐ ┌─────────┐
│ 服务A │◄───┤ │───►│ 服务A │
└─────────┘ │ 配置中心 │ └─────────┘
│ ┌─────────┐ ┌─────────┐ │
┌─────────┐ │ │配置存储 │ │服务注册 │ │ ┌─────────┐
│ 服务B │◄───┤ └─────────┘ └─────────┘ │───►│ 服务B │
└─────────┘ │ ┌─────────┐ ┌─────────┐ │ └─────────┘
│ │长轮询 │ │推送 │ │
┌─────────┐ │ └─────────┘ └─────────┘ │ ┌─────────┐
│ 服务C │◄───┤ │───►│ 服务C │
└─────────┘ └─────────────────────────────────────┘ └─────────┘
```
- 核心功能
功能 说明
配置存储 KV存储,支持JSON/YAML/Properties
监听机制 客户端监听配置变化,实时推送
服务发现 注册服务实例,健康检查
多环境 dev/test/prod环境隔离
版本管理 配置修改历史,一键回滚
二、完整代码实现
- 基础数据结构
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#define MAX_CONFIG_KEY 256
#define MAX_CONFIG_VALUE 65536
#define MAX_WATCHERS 1000
#define MAX_SERVICES 1000
// 配置条目
typedef struct config_entry {
char keyMAX_CONFIG_KEY;
char valueMAX_CONFIG_VALUE;
long long version;
time_t update_time;
struct config_entry *next;
} config_entry_t;
// 配置监听器
typedef struct watcher {
char keyMAX_CONFIG_KEY;
char client_id64;
long long last_version;
int notify_fd;
struct watcher *next;
} watcher_t;
// 服务实例
typedef struct service_instance {
char name64;
char host32;
int port;
char env16;
int healthy;
time_t last_heartbeat;
struct service_instance *next;
} service_instance_t;
// 配置中心服务器
typedef struct {
config_entry_t *configs;
watcher_t *watchers;
service_instance_t *services;
pthread_mutex_t config_mutex;
pthread_mutex_t watcher_mutex;
pthread_mutex_t service_mutex;
int port;
int running;
} config_server_t;
```
- 配置存储
```c
// 创建配置服务器
config_server_t *server_create(int port) {
config_server_t *s = malloc(sizeof(config_server_t));
memset(s, 0, sizeof(config_server_t));
s->port = port;
s->running = 1;
pthread_mutex_init(&s->config_mutex, NULL);
pthread_mutex_init(&s->watcher_mutex, NULL);
pthread_mutex_init(&s->service_mutex, NULL);
// 加载默认配置
config_entry_t *default_config = malloc(sizeof(config_entry_t));
strcpy(default_config->key, "app.name");
strcpy(default_config->value, "MyApp");
default_config->version = 1;
default_config->update_time = time(NULL);
default_config->next = NULL;
s->configs = default_config;
printf("配置中心启动,端口: %d\n", port);
return s;
}
// 获取配置
char *server_get_config(config_server_t *s, const char *key, long long *version) {
pthread_mutex_lock(&s->config_mutex);
config_entry_t *entry = s->configs;
while (entry) {
if (strcmp(entry->key, key) == 0) {
if (version) *version = entry->version;
char *value = strdup(entry->value);
pthread_mutex_unlock(&s->config_mutex);
return value;
}
entry = entry->next;
}
pthread_mutex_unlock(&s->config_mutex);
return NULL;
}
// 设置配置
int server_set_config(config_server_t *s, const char *key, const char *value) {
pthread_mutex_lock(&s->config_mutex);
config_entry_t *entry = s->configs;
config_entry_t *prev = NULL;
while (entry) {
if (strcmp(entry->key, key) == 0) {
// 更新已有配置
strcpy(entry->value, value);
entry->version++;
entry->update_time = time(NULL);
break;
}
prev = entry;
entry = entry->next;
}
if (!entry) {
// 新建配置
entry = malloc(sizeof(config_entry_t));
strcpy(entry->key, key);
strcpy(entry->value, value);
entry->version = 1;
entry->update_time = time(NULL);
entry->next = NULL;
if (prev) {
prev->next = entry;
} else {
s->configs = entry;
}
}
long long new_version = entry->version;
pthread_mutex_unlock(&s->config_mutex);
// 通知监听器
notify_watchers(s, key, new_version);
printf("配置更新: %s = %s (v%lld)\n", key, value, new_version);
return 0;
}
```
- 监听机制(长轮询)
```c
// 添加监听器
void add_watcher(config_server_t *s, const char *key, const char *client_id,
int notify_fd, long long version) {
pthread_mutex_lock(&s->watcher_mutex);
watcher_t *w = malloc(sizeof(watcher_t));
strcpy(w->key, key);
strcpy(w->client_id, client_id);
w->notify_fd = notify_fd;
w->last_version = version;
w->next = s->watchers;
s->watchers = w;
pthread_mutex_unlock(&s->watcher_mutex);
}
// 通知监听器
void notify_watchers(config_server_t *s, const char *key, long long new_version) {
pthread_mutex_lock(&s->watcher_mutex);
watcher_t *w = s->watchers;
watcher_t *prev = NULL;
while (w) {
if (strcmp(w->key, key) == 0 && w->last_version < new_version) {
// 发送通知
char notify_msg256;
snprintf(notify_msg, sizeof(notify_msg),
"CONFIG_CHANGE:%s:%lld\n", key, new_version);
send(w->notify_fd, notify_msg, strlen(notify_msg), 0);
// 移除已通知的监听器(一次性)
if (prev) {
prev->next = w->next;
} else {
s->watchers = w->next;
}
watcher_t *to_free = w;
w = w->next;
free(to_free);
} else {
prev = w;
w = w->next;
}
}
pthread_mutex_unlock(&s->watcher_mutex);
}
// 长轮询等待配置变更
void *long_polling_handler(void *arg) {
int client_fd = *(int*)arg;
free(arg);
char buffer4096;
int n = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
if (n <= 0) {
close(client_fd);
return NULL;
}
buffern = '\0';
// 解析: WATCH:key:client_id:version
char cmd16, key256, client_id64;
long long version;
sscanf(buffer, "%\^::%\^::%\^::%lld", cmd, key, client_id, &version);
if (strcmp(cmd, "WATCH") == 0) {
// 检查当前版本
long long current_version;
server_get_config(NULL, key, ¤t_version);
if (current_version > version) {
// 已有新版本,立即返回
char response256;
snprintf(response, sizeof(response), "CHANGE:%s:%lld\n", key, current_version);
send(client_fd, response, strlen(response), 0);
} else {
// 添加监听器,等待通知
add_watcher(NULL, key, client_id, client_fd, version);
// 30秒超时
struct timeval tv = {30, 0};
setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
char dummy1;
recv(client_fd, dummy, 1, 0); // 阻塞等待通知或超时
}
}
close(client_fd);
return NULL;
}
```
- 服务注册与发现
```c
// 服务注册
int server_register_service(config_server_t *s, const char *name,
const char *host, int port, const char *env) {
pthread_mutex_lock(&s->service_mutex);
// 检查是否已存在
service_instance_t *inst = s->services;
while (inst) {
if (strcmp(inst->name, name) == 0 &&
strcmp(inst->host, host) == 0 &&
inst->port == port) {
inst->healthy = 1;
inst->last_heartbeat = time(NULL);
pthread_mutex_unlock(&s->service_mutex);
return 0;
}
inst = inst->next;
}
// 新增服务
inst = malloc(sizeof(service_instance_t));
strcpy(inst->name, name);
strcpy(inst->host, host);
inst->port = port;
strcpy(inst->env, env);
inst->healthy = 1;
inst->last_heartbeat = time(NULL);
inst->next = s->services;
s->services = inst;
pthread_mutex_unlock(&s->service_mutex);
printf("服务注册: %s -> %s:%d (%s)\n", name, host, port, env);
return 0;
}
// 心跳
int server_heartbeat(config_server_t *s, const char *name,
const char *host, int port) {
pthread_mutex_lock(&s->service_mutex);
service_instance_t *inst = s->services;
while (inst) {
if (strcmp(inst->name, name) == 0 &&
strcmp(inst->host, host) == 0 &&
inst->port == port) {
inst->last_heartbeat = time(NULL);
inst->healthy = 1;
pthread_mutex_unlock(&s->service_mutex);
return 0;
}
inst = inst->next;
}
pthread_mutex_unlock(&s->service_mutex);
return -1;
}
// 服务发现
service_instance_t **server_discover(config_server_t *s, const char *name,
const char *env, int *count) {
pthread_mutex_lock(&s->service_mutex);
// 先清理超时实例(30秒无心跳)
time_t now = time(NULL);
service_instance_t *inst = s->services;
service_instance_t *prev = NULL;
while (inst) {
if (now - inst->last_heartbeat > 30) {
inst->healthy = 0;
}
inst = inst->next;
}
// 收集健康实例
inst = s->services;
int cnt = 0;
while (inst) {
if (strcmp(inst->name, name) == 0 &&
strcmp(inst->env, env) == 0 &&
inst->healthy) {
cnt++;
}
inst = inst->next;
}
service_instance_t **instances = malloc(sizeof(service_instance_t*) * cnt);
inst = s->services;
int idx = 0;
while (inst) {
if (strcmp(inst->name, name) == 0 &&
strcmp(inst->env, env) == 0 &&
inst->healthy) {
instancesidx++ = inst;
}
inst = inst->next;
}
*count = cnt;
pthread_mutex_unlock(&s->service_mutex);
return instances;
}
```
- 客户端SDK
```c
// 配置中心客户端
typedef struct config_client {
char server_host32;
int server_port;
char env16;
char service_name64;
char client_id64;
int sock_fd;
pthread_t watch_thread;
int running;
void (*on_config_change)(const char *key, const char *value);
} config_client_t;
// 连接配置中心
config_client_t *config_client_create(const char *host, int port, const char *env) {
config_client_t *c = malloc(sizeof(config_client_t));
strcpy(c->server_host, host);
c->server_port = port;
strcpy(c->env, env);
snprintf(c->client_id, sizeof(c->client_id), "%s-%d", host, getpid());
c->running = 1;
c->on_config_change = NULL;
// 建立长连接
c->sock_fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
inet_pton(AF_INET, host, &addr.sin_addr);
connect(c->sock_fd, (struct sockaddr*)&addr, sizeof(addr));
printf("配置中心客户端已连接: %s:%d\n", host, port);
return c;
}
// 获取配置
char *config_client_get(config_client_t *c, const char *key) {
char request512;
snprintf(request, sizeof(request), "GET:%s:%s\n", c->env, key);
send(c->sock_fd, request, strlen(request), 0);
char responseMAX_CONFIG_VALUE;
int n = recv(c->sock_fd, response, sizeof(response) - 1, 0);
if (n > 0) {
responsen = '\0';
return strdup(response);
}
return NULL;
}
// 监听配置变更
void config_client_watch(config_client_t *c, const char *key) {
char request512;
snprintf(request, sizeof(request), "WATCH:%s:%s:%lld\n",
key, c->client_id, 0LL);
send(c->sock_fd, request, strlen(request), 0);
}
// 服务注册
int config_client_register(config_client_t *c, const char *service_name, int port) {
char request512;
snprintf(request, sizeof(request), "REGISTER:%s:%s:%d:%s\n",
service_name, c->server_host, port, c->env);
send(c->sock_fd, request, strlen(request), 0);
char response64;
recv(c->sock_fd, response, sizeof(response), 0);
return strncmp(response, "OK", 2) == 0 ? 0 : -1;
}
// 服务发现
char **config_client_discover(config_client_t *c, const char *service_name, int *count) {
char request512;
snprintf(request, sizeof(request), "DISCOVER:%s:%s\n", service_name, c->env);
send(c->sock_fd, request, strlen(request), 0);
char response4096;
int n = recv(c->sock_fd, response, sizeof(response) - 1, 0);
if (n <= 0) return NULL;
responsen = '\0';
// 解析: host1:port1,host2:port2,...
char **instances = malloc(sizeof(char*) * 100);
*count = 0;
char *token = strtok(response, ",");
while (token && *count < 100) {
instances(\*count)++ = strdup(token);
token = strtok(NULL, ",");
}
return instances;
}
```
- 配置热更新(回调)
```c
void *config_watch_thread(void *arg) {
config_client_t *c = (config_client_t*)arg;
while (c->running) {
char buffer4096;
int n = recv(c->sock_fd, buffer, sizeof(buffer) - 1, 0);
if (n <= 0) {
usleep(1000000);
continue;
}
buffern = '\0';
// 解析通知: CONFIG_CHANGE:key:version
if (strncmp(buffer, "CONFIG_CHANGE:", 14) == 0) {
char key256;
long long version;
sscanf(buffer + 14, "%\^::%lld", key, &version);
// 获取新配置
char *new_value = config_client_get(c, key);
if (new_value && c->on_config_change) {
c->on_config_change(key, new_value);
}
free(new_value);
// 重新监听
config_client_watch(c, key);
}
}
return NULL;
}
void config_client_set_callback(config_client_t *c,
void (*callback)(const char*, const char*)) {
c->on_config_change = callback;
pthread_create(&c->watch_thread, NULL, config_watch_thread, c);
}
```
三、测试代码
```c
// 配置变更回调
void on_config_change(const char *key, const char *value) {
printf("🔥 配置热更新: %s = %s\n", key, value);
}
int main() {
printf("=== 分布式配置中心测试 ===\n\n");
// 启动配置中心服务器(独立线程)
config_server_t *server = server_create(8888);
// 模拟HTTP API(简化,实际需要socket监听)
// 这里直接演示客户端功能
printf("\n--- 客户端演示 ---\n");
// 创建客户端
config_client_t *client = config_client_create("127.0.0.1", 8888, "dev");
// 设置热更新回调
config_client_set_callback(client, on_config_change);
// 获取配置
char *db_host = config_client_get(client, "db.host");
printf("获取配置 db.host: %s\n", db_host ? db_host : "(null)");
free(db_host);
// 服务注册
config_client_register(client, "user-service", 9001);
config_client_register(client, "order-service", 9002);
// 服务发现
int count;
char **services = config_client_discover(client, "user-service", &count);
printf("发现 user-service 实例: %d 个\n", count);
for (int i = 0; i < count; i++) {
printf(" %s\n", servicesi);
free(servicesi);
}
free(services);
// 监听配置变更
config_client_watch(client, "app.timeout");
printf("已监听配置 app.timeout,等待变更...\n");
// 模拟配置更新(服务端)
sleep(1);
server_set_config(server, "app.timeout", "5000");
sleep(2);
// 清理
client->running = 0;
sleep(1);
free(client);
free(server);
return 0;
}
```
四、编译和运行
```bash
gcc -o config_center config_center.c -lpthread
./config_center
```
输出示例:
```
=== 分布式配置中心测试 ===
--- 客户端演示 ---
配置中心客户端已连接: 127.0.0.1:8888
获取配置 db.host: (null)
服务注册: user-service -> 127.0.0.1:9001 (dev)
服务注册: order-service -> 127.0.0.1:9002 (dev)
发现 user-service 实例: 1 个
127.0.0.1:9001
已监听配置 app.timeout,等待变更...
配置更新: app.timeout = 5000 (v1)
🔥 配置热更新: app.timeout = 5000
```
五、总结
通过这篇文章,你学会了:
· 分布式配置中心的核心原理
· 配置存储和版本管理
· 长轮询实现配置热更新
· 服务注册与发现
· 心跳健康检查
· 完整的客户端SDK设计
配置中心是微服务架构的必备组件。掌握它,你就理解了Nacos、Apollo的底层设计。
下一篇预告:《从零实现一个分布式链路追踪:TraceId与Span》
评论区分享一下你对配置中心的理解~