package consul import ( "fmt" "git.makemake.in/kzkzzzz/mycommon/myconf" "git.makemake.in/kzkzzzz/mycommon/myregistry" "github.com/google/uuid" api "github.com/hashicorp/consul/api" "log" "net" "net/url" "time" ) var _ myregistry.IRegister = (*Consul)(nil) type Consul struct { client *api.Client serviceIds map[string][]string serviceTags []string } func (c *Consul) Name() string { return "consul" } func (c *Consul) Register(service *myregistry.ServiceInfo) error { // 健康检查 serviceId := uuid.New().String() c.serviceIds[service.ServiceName] = append(c.serviceIds[service.ServiceName], serviceId) check := &api.AgentServiceCheck{ CheckID: serviceId, TCP: fmt.Sprintf("%s:%d", service.Ip, service.Port), Timeout: "5s", // 超时时间 Interval: "20s", // 运行检查的频率 // 指定时间后自动注销不健康的服务节点 // 最小超时时间为1分钟,收获不健康服务的进程每30秒运行一次,因此触发注销的时间可能略长于配置的超时时间。 DeregisterCriticalServiceAfter: "5m", Status: "passing", } srv := &api.AgentServiceRegistration{ ID: serviceId, // 服务唯一ID Name: service.ServiceName, // 服务名称 Tags: c.serviceTags, // 为服务打标签 Address: service.Ip, Port: service.Port, Check: check, } return c.client.Agent().ServiceRegister(srv) } func (c *Consul) Deregister(service *myregistry.ServiceInfo) error { for _, svcId := range c.serviceIds[service.ServiceName] { err := c.client.Agent().ServiceDeregister(svcId) if err != nil { log.Printf("Failed to deregister service %s: %s\n", service, err) } } return nil } type Conf struct { Addr string Token string } func MustNew(conf *myconf.Config) *Consul { consul, err := New(conf) if err != nil { panic(err) } return consul } func New(conf *myconf.Config) (*Consul, error) { cfg := api.DefaultConfig() cfg.Address = conf.GetString("addr") cfg.Transport.DialContext = (&net.Dialer{ Timeout: 3 * time.Second, KeepAlive: 20 * time.Second, DualStack: true, }).DialContext cfg.Token = conf.GetString("token") username := conf.GetString("username") password := conf.GetString("password") if username != "" && password != "" { cfg.HttpAuth = &api.HttpBasicAuth{ Username: username, Password: password, } } client, err := api.NewClient(cfg) if err != nil { return nil, err } cl := &Consul{ client: client, serviceIds: make(map[string][]string), serviceTags: make([]string, 0), } if v := conf.GetStringSlice("serviceTags"); len(v) > 0 { cl.serviceTags = v } else { cl.serviceTags = []string{} } return cl, nil } func (c *Consul) Client() *api.Client { return c.client } func GrpcUrl(serviceName string, conf *myconf.Config) string { return GrpcUrlWithTag("", serviceName, conf) } func GrpcUrlWithTag(tag string, serviceName string, conf *myconf.Config) string { u := &url.URL{ Scheme: schemeName, Host: conf.GetString("addr"), Path: serviceName, } query := u.Query() query.Set("healthy", "true") if v := conf.GetString("token"); v != "" { query.Set("token", v) } if tag != "" { query.Set("tag", tag) } username := conf.GetString("username") password := conf.GetString("password") if username != "" && password != "" { u.User = url.UserPassword(username, password) } u.RawQuery = query.Encode() return u.String() }