Go实现轻量级服务注册中心,基于内存存储+定时任务,支持注册、心跳续约、自动剔除和查询;定义ServiceInstance结构体,用map按服务名索引实例,HTTP提供/register和/heartbeat/{id}接口,每10秒扫描剔除超时30秒未心跳的实例。
用 Go 实现一个轻量级服务注册中心,核心是维护服务实例的注册、心跳续约、过期剔除和查询能力。不需要依赖外部存储,用内存 + 定时任务就能支撑中小规模场景。
每个服务实例需要唯一标识(如 service-name#ip:port),并记录地址、健康状态、最后心跳时间等字段:
```go
type ServiceInstance struct {
ID string `json:"id"`
ServiceName string `json:"service_name"`
Addr string `json:"addr"`
Status string `json:"status"` // "UP", "DOWN"
LastHeartAt time.Time `json:"last_heart_at"`
}
// 用 map 做服务名到实例列表的索引,支持快速按服务名查所有实例
var registry = make(map[string][]*ServiceInstance)
var mu sync.RWMutex
```
用 net/http 暴露两个基础接口:
LastHeartAt,标记为 UP;ID 可设计为 service-name#ip:port
示例处理逻辑:
```go
http.HandleFunc("/register", func(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var ins ServiceInstance
if err := json.NewDecoder(r.Body).Decode(&ins); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
if ins.ServiceName == "" || ins.Addr == "" {
http.Error(w, "Missing required fields", http.StatusBadRequest)
return
}
ins.ID = fmt.Sprintf("%s#%s", ins.ServiceName, ins.Addr)
ins.Status = "UP"
ins.LastHeartAt = time.Now()
mu.Lock()
defer mu.Unlock()
registry[ins.ServiceName] = append(registry[ins.ServiceName], &ins)
w.WriteHeader(http.StatusOK)
})
```
心跳超时(如 30 秒没更新)的实例应被自动设为 DOWN 或移除。启动一个 goroutine 每 10 秒扫描一次:
```go
go func() {
ticker := time.NewTicker(10 * time.Second)
defer ticker.Stop()
for range ticker.C {
mu.Lock()
now := time.Now()
for svcName, instances := range registry {
filtered := make([]*ServiceInstance, 0, len(instances))
for _, ins := range instances {
if now.Sub(ins.LastHeartAt) filtered = append(filtered, ins)
} else {
ins.Status = "DOWN"
}
}
registry[svcName] = filtered
}
mu.Unlock()
}
}()
```
供客户端拉取可用实例列表,支持按服务名过滤,并只返回 Status == "UP" 的实例:
```go
http.HandleFunc("/services/", func(w http.ResponseWriter, r *http.Request) {
svcName :
= strings.TrimPrefix(r.URL.Path, "/services/")
if svcName == "" {
http.Error(w, "Service name required", http.StatusBadRequest)
return
}
mu.RLock()
instances := registry[svcName]
mu.RUnlock()
upInstances := make([]*ServiceInstance, 0)
for _, ins := range instances {
if ins.Status == "UP" {
upInstances = append(upInstances, ins)
}
}
json.NewEncoder(w).Encode(upInstances)
})
```
不复杂但容易忽略的是并发安全和超时判断粒度——读多写少场景推荐 sync.RWMutex,心跳超时值需与客户端上报频率对齐。后续可扩展 etcd 存储、TLS 支持或健康检查探针。