// Package registrar is for the service discovery system package registrar import ( "context" "fmt" "time" clientv3 "go.etcd.io/etcd/client/v3" ) type ServiceRegistrar struct { client *clientv3.Client leaseID clientv3.LeaseID ttl int64 key string cancel context.CancelFunc } // NewServiceRegistrar creates a registrar with the given etcd client func NewServiceRegistrar(endpoints []string) (*ServiceRegistrar, error) { cfg := clientv3.Config{ Endpoints: endpoints, DialTimeout: 5 * time.Second, } cli, err := clientv3.New(cfg) if err != nil { return nil, err } return &ServiceRegistrar{client: cli}, nil } // Register service with etcd using TTL lease func (r *ServiceRegistrar) Register(serviceName string, servicePart string, instanceID string, host string, port int, ttlSeconds int64) error { r.ttl = ttlSeconds ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() r.key = fmt.Sprintf("/services/%s/%s/%s", serviceName, servicePart, instanceID) // Create lease leaseResp, err := r.client.Grant(ctx, r.ttl) if err != nil { return err } r.leaseID = leaseResp.ID // Put key with lease _, err = r.client.Put(ctx, r.key, fmt.Sprintf("%s:%d", host, port), clientv3.WithLease(r.leaseID)) if err != nil { return err } // Keep alive context kaCtx, kaCancel := context.WithCancel(context.Background()) r.cancel = kaCancel // Keep lease alive go func() { ch, err := r.client.KeepAlive(kaCtx, r.leaseID) if err != nil { fmt.Printf("KeepAlive error: %v\n", err) return } for range ch { // optionally inspect TTL updates here } }() return nil } // Deregister removes the registered key and stops keep-alive func (r *ServiceRegistrar) Deregister() error { if r.cancel != nil { r.cancel() } _, err := r.client.Delete(context.Background(), r.key) return err }