diff --git a/user-server/main.go b/user-server/main.go new file mode 100644 index 0000000..730d2a5 --- /dev/null +++ b/user-server/main.go @@ -0,0 +1,144 @@ +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("服务已停止") +}