Codebase list golang-github-go-kit-kit / 4b48129 sd / etcd / registrar.go
4b48129

Tree @4b48129 (Download .tar.gz)

registrar.go @4b48129raw · history · blame

package etcd

import (
	etcd "github.com/coreos/etcd/client"

	"github.com/go-kit/kit/log"
	"time"
)

const (
	MinHeartBeatTime = time.Millisecond * 500
)

// Registrar registers service instance liveness information to etcd.
type Registrar struct {
	client  Client
	service Service
	logger  log.Logger
	quit    chan struct{}
}

// Service holds the instance identifying data you want to publish to etcd. Key
// must be unique, and value is the string returned to subscribers, typically
// called the "instance" string in other parts of package sd.
type Service struct {
	Key           string // unique key, e.g. "/service/foobar/1.2.3.4:8080"
	Value         string // returned to subscribers, e.g. "http://1.2.3.4:8080"
	TTL           *TTLOption
	DeleteOptions *etcd.DeleteOptions
}

// TTLOption allow setting a key with a TTL, and regularly refreshes the lease with a goroutine
type TTLOption struct {
	Heartbeat time.Duration
	TTL       time.Duration
}

// NewTTLOption returns a TTLOption
func NewTTLOption(heartbeat, ttl time.Duration) *TTLOption {
	if heartbeat <= MinHeartBeatTime {
		heartbeat = MinHeartBeatTime
	}
	if ttl <= heartbeat {
		ttl = heartbeat * 3
	}
	return &TTLOption{heartbeat, ttl}
}

// NewRegistrar returns a etcd Registrar acting on the provided catalog
// registration (service).
func NewRegistrar(client Client, service Service, logger log.Logger) *Registrar {
	return &Registrar{
		client:  client,
		service: service,
		logger: log.NewContext(logger).With(
			"key", service.Key,
			"value", service.Value,
		),
	}
}

// Register implements the sd.Registrar interface. Call it when you want your
// service to be registered in etcd, typically at startup.
func (r *Registrar) Register() {
	if err := r.client.Register(r.service); err != nil {
		r.logger.Log("err", err)
	} else {
		r.logger.Log("action", "register")
	}
	if r.service.TTL == nil {
		return
	}
	r.quit = make(chan struct{})
	go func() {
		for {
			select {
			case <-r.quit:
				return
			case <-time.After(r.service.TTL.Heartbeat):
				if err := r.client.Register(r.service); err != nil {
					r.logger.Log("err", err)
				}
			}
		}
	}()
}

// Deregister implements the sd.Registrar interface. Call it when you want your
// service to be deregistered from etcd, typically just prior to shutdown.
func (r *Registrar) Deregister() {
	if err := r.client.Deregister(r.service); err != nil {
		r.logger.Log("err", err)
	} else {
		r.logger.Log("action", "deregister")
	}
	if r.quit != nil {
		close(r.quit)
	}
}