从零实现一个分布式配置中心:服务发现与热更新

前言

你有没有想过:微服务架构中,几十上百个服务怎么统一管理配置?改一个数据库密码,难道要重启所有服务?

配置中心解决了这个问题:配置集中管理、动态刷新、服务实时感知。

今天我们用C语言从零实现一个分布式配置中心:

· 配置存储(KV)

· 服务注册与发现

· 配置热更新(长轮询/推送)

· 多环境隔离(dev/prod)

· 版本管理(回滚支持)

· 完整的客户端SDK


一、配置中心核心原理

  1. 架构图

```

┌─────────┐ ┌─────────────────────────────────────┐ ┌─────────┐

│ 服务A │◄───┤ │───►│ 服务A │

└─────────┘ │ 配置中心 │ └─────────┘

│ ┌─────────┐ ┌─────────┐ │

┌─────────┐ │ │配置存储 │ │服务注册 │ │ ┌─────────┐

│ 服务B │◄───┤ └─────────┘ └─────────┘ │───►│ 服务B │

└─────────┘ │ ┌─────────┐ ┌─────────┐ │ └─────────┘

│ │长轮询 │ │推送 │ │

┌─────────┐ │ └─────────┘ └─────────┘ │ ┌─────────┐

│ 服务C │◄───┤ │───►│ 服务C │

└─────────┘ └─────────────────────────────────────┘ └─────────┘

```

  1. 核心功能

功能 说明

配置存储 KV存储,支持JSON/YAML/Properties

监听机制 客户端监听配置变化,实时推送

服务发现 注册服务实例,健康检查

多环境 dev/test/prod环境隔离

版本管理 配置修改历史,一键回滚


二、完整代码实现

  1. 基础数据结构

```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;

```

  1. 配置存储

```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;

}

```

  1. 监听机制(长轮询)

```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, &current_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;

}

```

  1. 服务注册与发现

```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;

}

```

  1. 客户端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;

}

```

  1. 配置热更新(回调)

```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》


评论区分享一下你对配置中心的理解~

相关推荐
省四收割者1 小时前
从硬件中断到分布式协程:全景解构高并发机制与 C / Golang 的巅峰对决
c++·分布式·嵌入式硬件·golang
Cx330❀1 小时前
【Linux网络】从零定制应用层协议:黏包问题、全双工缓冲区与 Jsoncpp 序列化深度解析
linux·运维·服务器·开发语言·网络·c++·人工智能
江屿风1 小时前
C++图论基础Bellman-Ford与spfa算法如何判断负环
开发语言·c++·笔记·算法·图论
森G1 小时前
68、项目配置和示例---------多媒体
c++·qt
进击的荆棘1 小时前
优选算法——BFS
c++·算法·leetcode·宽度优先
.千余3 小时前
【C++】C++ set 与 multiset 完全指南:关联式容器入门
开发语言·c++·笔记·学习·其他
c++之路6 小时前
CMake 系列教程(二):基础命令详解
开发语言·c++
南境十里·墨染春水10 小时前
C++ 工厂模式:从入门到进阶,彻底掌握对象创建的艺术
开发语言·c++·算法
一拳一个呆瓜13 小时前
【STL】_SCL_SECURE_NO_WARNINGS
c++·stl