17370845950

如何使用Golang实现服务注册中心_维护服务列表和状态
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
```

提供 HTTP 注册与心跳接口

net/http 暴露两个基础接口:

  • POST /register:接收 JSON,校验必填字段后存入 registry;若实例已存在,仅更新时间戳和状态
  • PUT /heartbeat/{id}:根据 ID 查找实例,更新 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 支持或健康检查探针。