package main import ( "context" "log" "net" "os" "os/signal" "syscall" "time" "go.etcd.io/etcd/client/v3" "go.etcd.io/etcd/client/v3/naming/endpoints" ) // UserServer 用户服务结构体 type UserServer struct{} // Login 登录方法示例 func (u *UserServer) Login(username string, password string) string { if username == "admin" && password == "123456" { return "登录成功,token: abc123" } return "登录失败,用户名或密码错误" } // 服务注册到etcd func registerService(etcdAddr []string, serviceName, serviceAddr string) (*clientv3.Client, error) { // 1. 创建etcd客户端(返回客户端,方便后续注销) client, err := clientv3.New(clientv3.Config{ Endpoints: etcdAddr, DialTimeout: 5 * time.Second, }) if err != nil { return nil, err } // 2. 创建租约(10秒有效期) leaseResp, err := client.Grant(context.Background(), 10) if err != nil { client.Close() return nil, err } leaseID := leaseResp.ID // 3. 启动租约续约 keepAliveChan, err := client.KeepAlive(context.Background(), leaseID) if err != nil { client.Revoke(context.Background(), leaseID) // 清理租约 client.Close() return nil, err } // 后台处理续约结果 go func() { for { select { case keepAliveResp, ok := <-keepAliveChan: if !ok { // 续约通道关闭,尝试重新连接 log.Println("租约续约通道关闭,尝试重新连接...") return } log.Printf("服务续约成功,租约ID: %v", keepAliveResp.ID) } } }() // 4. 注册服务到etcd em, err := endpoints.NewManager(client, "/services/"+serviceName) if err != nil { client.Revoke(context.Background(), leaseID) client.Close() return nil, err } // 注册服务并绑定租约 err = em.AddEndpoint( context.Background(), "/services/"+serviceName+"/"+serviceAddr, endpoints.Endpoint{Addr: serviceAddr}, clientv3.WithLease(leaseID), ) if err != nil { client.Revoke(context.Background(), leaseID) client.Close() return nil, err } return client, nil } // 从etcd注销服务 func unregisterService(client *clientv3.Client, serviceName, serviceAddr string) { if client == nil { return } defer client.Close() em, err := endpoints.NewManager(client, "/services/"+serviceName) if err != nil { log.Printf("注销服务失败: %v", err) return } // 主动删除服务节点 err = em.DeleteEndpoint(context.Background(), "/services/"+serviceName+"/"+serviceAddr) if err != nil { log.Printf("删除服务节点失败: %v", err) } else { log.Println("服务已从etcd注销") } } func main() { // 服务配置 etcdAddr := []string{"http://127.0.0.1:2379"} serviceName := "user-service" serviceAddr := "127.0.0.1:8080" // 服务监听地址 // 注册服务到etcd etcdClient, err := registerService(etcdAddr, serviceName, serviceAddr) if err != nil { log.Fatalf("服务注册失败: %v", err) } log.Printf("服务 %s 已注册到etcd,地址: %s", serviceName, serviceAddr) // 启动一个实际的服务监听(例如TCP服务),避免程序立即退出 listener, err := net.Listen("tcp", serviceAddr) if err != nil { log.Fatalf("服务监听失败: %v", err) } defer listener.Close() log.Printf("服务已启动,监听地址: %s", serviceAddr) // 优雅退出处理(捕获中断信号,注销服务) sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) <-sigChan // 等待中断信号 // 程序退出前注销服务 unregisterService(etcdClient, serviceName, serviceAddr) log.Println("服务已停止") }