从零实现一个服务注册中心:Eureka与Consul

前言

你有没有想过:在微服务架构中,几十上百个服务是怎么相互发现的?新服务上线怎么让其他服务知道?服务下线怎么通知?

服务注册中心是微服务架构的"通讯录",所有服务在这里注册和发现。

今天我们用C语言从零实现一个服务注册中心:

· 服务注册与注销

· 服务发现(拉取/推送)

· 心跳健康检查

· 服务缓存与更新

· 保护模式(防止雪崩)

· 集群同步


一、服务注册中心核心原理

  1. 架构图

```

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

│ 服务注册中心集群 │

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

│ │ 节点1 │◄─│ 节点2 │─►│ 节点3 │ │

│ │ (主) │ │ (从) │ │ (从) │ │

│ └──────┬──────┘ └─────────────┘ └─────────────┘ │

│ │ │

│ ▼ │

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

│ │ 服务注册表 │ │

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

│ │ │ 服务A │ │ 服务B │ │ 服务C │ │ │

│ │ │ 实例1 │ │ 实例1 │ │ 实例1 │ │ │

│ │ │ 实例2 │ │ 实例2 │ │ 实例2 │ │ │

│ │ └──────────┘ └──────────┘ └──────────┘ │ │

│ └─────────────────────────────────────────────────────┘ │

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

│ │ │

▼ ▼ ▼

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

│ 服务A │ │ 服务B │ │ 服务C │

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

```

  1. 核心功能

功能 说明

服务注册 服务启动时注册自己

服务发现 获取其他服务地址列表

心跳检测 定期发送心跳保活

健康检查 剔除不健康实例

保护模式 防止网络抖动导致误剔除

集群同步 多节点数据一致性


二、完整代码实现

  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>

#define MAX_SERVICE_NAME 64

#define MAX_INSTANCE_ID 128

#define MAX_HOST 32

#define MAX_METADATA 256

#define MAX_INSTANCES 1000

// 服务实例状态

typedef enum {

INSTANCE_UP = 0,

INSTANCE_DOWN,

INSTANCE_STARTING,

INSTANCE_OUT_OF_SERVICE

} instance_status_t;

// 服务实例

typedef struct service_instance {

char instance_idMAX_INSTANCE_ID;

char service_nameMAX_SERVICE_NAME;

char hostMAX_HOST;

int port;

int secure_port;

instance_status_t status;

char metadataMAX_METADATA;

time_t register_time;

time_t last_heartbeat;

time_t last_dirty_time;

int renewal_count;

struct service_instance *next;

} service_instance_t;

// 服务

typedef struct service {

char nameMAX_SERVICE_NAME;

service_instance_t *instances;

int instance_count;

struct service *next;

} service_t;

// 注册中心

typedef struct registry_center {

service_t *services;

int enable_self_preservation;

int renewal_threshold;

int renewal_percent_threshold;

pthread_mutex_t mutex;

int port;

int running;

pthread_t eviction_thread;

} registry_center_t;

```

  1. 核心注册逻辑

```c

// 创建注册中心

registry_center_t *registry_create(int port) {

registry_center_t *rc = malloc(sizeof(registry_center_t));

memset(rc, 0, sizeof(registry_center_t));

rc->port = port;

rc->enable_self_preservation = 1;

rc->renewal_threshold = 10;

rc->renewal_percent_threshold = 85;

rc->running = 1;

pthread_mutex_init(&rc->mutex, NULL);

printf("服务注册中心启动,端口: %d\n", port);

return rc;

}

// 生成实例ID

void generate_instance_id(const char *service_name, const char *host, int port, char *id) {

snprintf(id, MAX_INSTANCE_ID, "%s:%s:%d", service_name, host, port);

}

// 注册服务

int registry_register(registry_center_t *rc, const char *service_name,

const char *host, int port, const char *metadata) {

pthread_mutex_lock(&rc->mutex);

// 查找服务

service_t *svc = rc->services;

while (svc) {

if (strcmp(svc->name, service_name) == 0) break;

svc = svc->next;

}

if (!svc) {

svc = malloc(sizeof(service_t));

strcpy(svc->name, service_name);

svc->instances = NULL;

svc->instance_count = 0;

svc->next = rc->services;

rc->services = svc;

}

// 检查实例是否已存在

service_instance_t *inst = svc->instances;

char instance_idMAX_INSTANCE_ID;

generate_instance_id(service_name, host, port, instance_id);

while (inst) {

if (strcmp(inst->instance_id, instance_id) == 0) {

// 更新现有实例

inst->last_heartbeat = time(NULL);

inst->status = INSTANCE_UP;

pthread_mutex_unlock(&rc->mutex);

printf("注册 更新实例: %s\n", instance_id);

return 0;

}

inst = inst->next;

}

// 创建新实例

inst = malloc(sizeof(service_instance_t));

strcpy(inst->instance_id, instance_id);

strcpy(inst->service_name, service_name);

strcpy(inst->host, host);

inst->port = port;

inst->status = INSTANCE_UP;

if (metadata) strcpy(inst->metadata, metadata);

inst->register_time = time(NULL);

inst->last_heartbeat = time(NULL);

inst->last_dirty_time = 0;

inst->renewal_count = 0;

inst->next = svc->instances;

svc->instances = inst;

svc->instance_count++;

pthread_mutex_unlock(&rc->mutex);

printf("注册 新实例: %s (%s:%d)\n", service_name, host, port);

return 0;

}

// 续约(心跳)

int registry_renew(registry_center_t *rc, const char *instance_id) {

pthread_mutex_lock(&rc->mutex);

service_t *svc = rc->services;

while (svc) {

service_instance_t *inst = svc->instances;

while (inst) {

if (strcmp(inst->instance_id, instance_id) == 0) {

inst->last_heartbeat = time(NULL);

inst->renewal_count++;

pthread_mutex_unlock(&rc->mutex);

return 0;

}

inst = inst->next;

}

svc = svc->next;

}

pthread_mutex_unlock(&rc->mutex);

return -1; // 实例不存在

}

// 注销服务

int registry_cancel(registry_center_t *rc, const char *instance_id) {

pthread_mutex_lock(&rc->mutex);

service_t *svc = rc->services;

while (svc) {

service_instance_t *inst = svc->instances;

service_instance_t *prev = NULL;

while (inst) {

if (strcmp(inst->instance_id, instance_id) == 0) {

if (prev) {

prev->next = inst->next;

} else {

svc->instances = inst->next;

}

svc->instance_count--;

free(inst);

pthread_mutex_unlock(&rc->mutex);

printf("注销 实例: %s\n", instance_id);

return 0;

}

prev = inst;

inst = inst->next;

}

svc = svc->next;

}

pthread_mutex_unlock(&rc->mutex);

return -1;

}

```

  1. 服务发现

```c

// 获取服务实例列表

service_instance_t **registry_discover(registry_center_t *rc,

const char *service_name, int *count) {

pthread_mutex_lock(&rc->mutex);

service_t *svc = rc->services;

while (svc) {

if (strcmp(svc->name, service_name) == 0) break;

svc = svc->next;

}

if (!svc) {

*count = 0;

pthread_mutex_unlock(&rc->mutex);

return NULL;

}

// 统计UP实例

service_instance_t *inst = svc->instances;

int up_count = 0;

while (inst) {

if (inst->status == INSTANCE_UP) up_count++;

inst = inst->next;

}

if (up_count == 0) {

*count = 0;

pthread_mutex_unlock(&rc->mutex);

return NULL;

}

service_instance_t **result = malloc(sizeof(service_instance_t*) * up_count);

inst = svc->instances;

int idx = 0;

while (inst) {

if (inst->status == INSTANCE_UP) {

resultidx++ = inst;

}

inst = inst->next;

}

*count = up_count;

pthread_mutex_unlock(&rc->mutex);

return result;

}

```

  1. 健康检查与剔除

```c

// 剔除过期实例

void registry_evict(registry_center_t *rc) {

pthread_mutex_lock(&rc->mutex);

time_t now = time(NULL);

int evicted = 0;

int lease_expiration_duration = 90; // 90秒无心跳剔除

service_t *svc = rc->services;

while (svc) {

service_instance_t *inst = svc->instances;

service_instance_t *prev = NULL;

while (inst) {

int expired = (now - inst->last_heartbeat) > lease_expiration_duration;

// 保护模式:防止误剔除

if (expired && rc->enable_self_preservation) {

// 检查整体续约率

// 如果低于阈值,进入保护模式

// 简化:不剔除

if (rc->renewal_count < rc->renewal_threshold) {

expired = 0;

}

}

if (expired) {

inst->status = INSTANCE_DOWN;

// 从链表中移除

if (prev) {

prev->next = inst->next;

} else {

svc->instances = inst->next;

}

svc->instance_count--;

printf("剔除 过期实例: %s\n", inst->instance_id);

free(inst);

evicted++;

inst = prev ? prev->next : svc->instances;

continue;

}

prev = inst;

inst = inst->next;

}

svc = svc->next;

}

pthread_mutex_unlock(&rc->mutex);

}

// 剔除线程

void *eviction_thread(void *arg) {

registry_center_t *rc = (registry_center_t*)arg;

while (rc->running) {

sleep(60); // 每分钟检查一次

registry_evict(rc);

}

return NULL;

}

```

  1. 客户端SDK

```c

// 注册中心客户端

typedef struct registry_client {

char registry_hostMAX_HOST;

int registry_port;

char service_nameMAX_SERVICE_NAME;

char instance_idMAX_INSTANCE_ID;

char hostMAX_HOST;

int port;

int running;

pthread_t heartbeat_thread;

} registry_client_t;

// 创建客户端

registry_client_t *registry_client_create(const char *registry_host, int registry_port,

const char *service_name, const char *host, int port) {

registry_client_t *client = malloc(sizeof(registry_client_t));

strcpy(client->registry_host, registry_host);

client->registry_port = registry_port;

strcpy(client->service_name, service_name);

strcpy(client->host, host);

client->port = port;

client->running = 1;

generate_instance_id(service_name, host, port, client->instance_id);

return client;

}

// 注册到注册中心

int registry_client_register(registry_client_t *client) {

// 实际通过HTTP/RPC调用注册中心

printf("客户端 注册服务: %s (%s:%d)\n",

client->service_name, client->host, client->port);

return 0;

}

// 心跳线程

void *heartbeat_thread_func(void *arg) {

registry_client_t *client = (registry_client_t*)arg;

while (client->running) {

sleep(30); // 每30秒发送心跳

printf("客户端 心跳: %s\n", client->instance_id);

// 实际调用注册中心续约接口

}

return NULL;

}

// 启动客户端

void registry_client_start(registry_client_t *client) {

registry_client_register(client);

pthread_create(&client->heartbeat_thread, NULL, heartbeat_thread_func, client);

}

// 停止客户端

void registry_client_stop(registry_client_t *client) {

client->running = 0;

pthread_join(client->heartbeat_thread, NULL);

// 实际调用注册中心注销接口

printf("客户端 注销: %s\n", client->instance_id);

free(client);

}

```

  1. 测试代码

```c

void test_registry() {

printf("=== 服务注册中心测试 ===\n");

// 创建注册中心

registry_center_t *rc = registry_create(8761);

// 启动剔除线程

pthread_t evict_tid;

pthread_create(&evict_tid, NULL, eviction_thread, rc);

// 注册服务

registry_register(rc, "user-service", "192.168.1.10", 8080, "version=1.0");

registry_register(rc, "user-service", "192.168.1.11", 8080, "version=1.0");

registry_register(rc, "order-service", "192.168.1.20", 8080, "version=1.0");

// 续约

char id1MAX_INSTANCE_ID, id2MAX_INSTANCE_ID;

generate_instance_id("user-service", "192.168.1.10", 8080, id1);

generate_instance_id("order-service", "192.168.1.20", 8080, id2);

for (int i = 0; i < 3; i++) {

registry_renew(rc, id1);

registry_renew(rc, id2);

sleep(10);

}

// 服务发现

int count;

service_instance_t **instances = registry_discover(rc, "user-service", &count);

printf("\n=== user-service 实例 ===\n");

for (int i = 0; i < count; i++) {

printf(" %s:%d (状态: %d)\n",

instancesi->host, instancesi->port, instancesi->status);

}

free(instances);

// 模拟客户端

printf("\n=== 客户端测试 ===\n");

registry_client_t *client = registry_client_create("127.0.0.1", 8761,

"test-service", "192.168.1.100", 9090);

registry_client_start(client);

sleep(10);

registry_client_stop(client);

rc->running = 0;

pthread_join(evict_tid, NULL);

free(rc);

}

int main() {

test_registry();

return 0;

}

```


三、编译和运行

```bash

gcc -o registry registry.c -lpthread

./registry

```


四、Eureka vs Consul vs 本实现

特性 本实现 Eureka Consul

服务注册 ✅ ✅ ✅

服务发现 ✅ ✅ ✅

心跳检测 ✅ ✅ ✅

保护模式 ✅ ✅ ❌

集群同步 ❌ ✅ ✅

KV存储 ❌ ❌ ✅

健康检查 ✅ ✅ ✅


五、总结

通过这篇文章,你学会了:

· 服务注册中心的核心原理

· 服务注册与注销

· 心跳续约机制

· 健康检查与剔除

· 保护模式防止雪崩

· 客户端SDK设计

服务注册中心是微服务架构的基石。掌握它,你就理解了Spring Cloud Eureka、Consul的底层设计。

下一篇预告:《从零实现一个配置管理平台:Apollo与Nacos》


评论区分享一下你用过什么服务注册中心~