update
This commit is contained in:
@@ -4,19 +4,30 @@ import (
|
||||
"fmt"
|
||||
"git.makemake.in/kzkzzzz/mycommon/myconf"
|
||||
"git.makemake.in/kzkzzzz/mycommon/mygrpc"
|
||||
"git.makemake.in/kzkzzzz/mycommon/mylog"
|
||||
"git.makemake.in/kzkzzzz/mycommon/myregistry"
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/consul/api/watch"
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/rs/xid"
|
||||
"log"
|
||||
"go.uber.org/zap"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ myregistry.IRegister = (*Consul)(nil)
|
||||
var (
|
||||
_ myregistry.IRegister = (*Consul)(nil)
|
||||
|
||||
defaultLog = mylog.NewLogger(&mylog.Config{
|
||||
Level: mylog.DebugLevel,
|
||||
NeedLogFile: false,
|
||||
ConsoleWriter: os.Stdout,
|
||||
ZapOpt: []zap.Option{
|
||||
zap.AddCaller(), zap.AddCallerSkip(1),
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
type Consul struct {
|
||||
client *api.Client
|
||||
@@ -42,7 +53,7 @@ func (c *Consul) Register(service *myregistry.ServiceInfo) error {
|
||||
serviceId := xid.New().String()
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
log.Printf("get hostname err: %s", err)
|
||||
defaultLog.Errorf("get hostname err: %s", err)
|
||||
} else {
|
||||
serviceId = fmt.Sprintf("%s-%s", hostname, serviceId)
|
||||
}
|
||||
@@ -57,12 +68,20 @@ func (c *Consul) Register(service *myregistry.ServiceInfo) error {
|
||||
// 指定时间后自动注销不健康的服务节点
|
||||
// 最小超时时间为1分钟,收获不健康服务的进程每30秒运行一次,因此触发注销的时间可能略长于配置的超时时间。
|
||||
DeregisterCriticalServiceAfter: "6m",
|
||||
Status: "passing",
|
||||
Status: api.HealthPassing,
|
||||
}
|
||||
|
||||
regTags := make([]string, len(c.serviceTags))
|
||||
copy(regTags, c.serviceTags)
|
||||
|
||||
if v := service.Extend["tag"]; v != "" {
|
||||
regTags = append(regTags, v)
|
||||
}
|
||||
|
||||
svc := &api.AgentServiceRegistration{
|
||||
ID: serviceId, // 服务唯一ID
|
||||
Name: service.ServiceName, // 服务名称
|
||||
Tags: c.serviceTags, // 为服务打标签
|
||||
Tags: regTags, // 为服务打标签
|
||||
Address: service.Ip,
|
||||
Port: service.Port,
|
||||
Check: check,
|
||||
@@ -70,14 +89,22 @@ func (c *Consul) Register(service *myregistry.ServiceInfo) error {
|
||||
|
||||
c.services = append(c.services, svc)
|
||||
|
||||
return c.client.Agent().ServiceRegister(svc)
|
||||
err = c.client.Agent().ServiceRegister(svc)
|
||||
if err != nil {
|
||||
defaultLog.Errorf("retry register service err: %s: %s", svc.Name, err)
|
||||
return err
|
||||
}
|
||||
|
||||
defaultLog.Infof("retry register service ok: %s", svc.Name)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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)
|
||||
defaultLog.Errorf("Failed to deregister service %s: %s\n", service, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -92,32 +119,12 @@ func MustNew(conf *myconf.Config, opts ...Opt) *Consul {
|
||||
}
|
||||
|
||||
func New(conf *myconf.Config, opts ...Opt) (*Consul, error) {
|
||||
|
||||
cfg := api.DefaultConfig()
|
||||
cfg.Address = conf.GetString("addr")
|
||||
|
||||
if cfg.Address == "" {
|
||||
return nil, fmt.Errorf("consul address is empty")
|
||||
}
|
||||
|
||||
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)
|
||||
client, err := NewClient(&ClientConfig{
|
||||
Address: conf.GetString("addr"),
|
||||
Token: conf.GetString("token"),
|
||||
Username: conf.GetString("username"),
|
||||
Password: conf.GetString("password"),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -138,39 +145,108 @@ func New(conf *myconf.Config, opts ...Opt) (*Consul, error) {
|
||||
return cl, nil
|
||||
}
|
||||
|
||||
func (c *Consul) healthCheck() {
|
||||
wlog := newWatchLogger()
|
||||
type ClientConfig struct {
|
||||
Address string
|
||||
Token string
|
||||
Username string
|
||||
Password string
|
||||
AutoGrpcPrefix bool
|
||||
}
|
||||
|
||||
wp, err := watch.Parse(map[string]any{
|
||||
"type": "services",
|
||||
})
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("parse watch err: %s", err))
|
||||
var (
|
||||
clientLock = &sync.Mutex{}
|
||||
clientMap = make(map[string]*api.Client)
|
||||
)
|
||||
|
||||
func NewClient(clientCfg *ClientConfig) (*api.Client, error) {
|
||||
clientLock.Lock()
|
||||
defer clientLock.Unlock()
|
||||
|
||||
if clientCfg.Address == "" {
|
||||
return nil, fmt.Errorf("consul address is empty")
|
||||
}
|
||||
|
||||
wp.Handler = func(u uint64, raw any) {
|
||||
if wlog.isWatchErr == true {
|
||||
if client, ok := clientMap[clientCfg.Address]; ok {
|
||||
return client, nil
|
||||
}
|
||||
|
||||
for _, svc := range c.services {
|
||||
//c.client.Agent().ServiceDeregister(svc.ID)
|
||||
err := c.client.Agent().ServiceRegister(svc)
|
||||
if err != nil {
|
||||
log.Printf("retry register service err: %s: %s", svc.Name, err)
|
||||
} else {
|
||||
log.Printf("retry register service ok: %s", svc.Name)
|
||||
cfg := api.DefaultConfig()
|
||||
cfg.Address = clientCfg.Address
|
||||
|
||||
if cfg.Address == "" {
|
||||
return nil, fmt.Errorf("consul address is empty")
|
||||
}
|
||||
|
||||
cfg.Transport.DialContext = (&net.Dialer{
|
||||
Timeout: 3 * time.Second,
|
||||
KeepAlive: 20 * time.Second,
|
||||
}).DialContext
|
||||
cfg.Token = clientCfg.Token
|
||||
|
||||
username := clientCfg.Username
|
||||
password := clientCfg.Password
|
||||
|
||||
if username != "" && password != "" {
|
||||
cfg.HttpAuth = &api.HttpBasicAuth{
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
}
|
||||
|
||||
client, err := api.NewClient(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clientMap[clientCfg.Address] = client
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (c *Consul) healthCheck() {
|
||||
var (
|
||||
lastIndex uint64
|
||||
isFirst = true
|
||||
err error
|
||||
meta *api.QueryMeta
|
||||
)
|
||||
|
||||
for {
|
||||
if isFirst == false {
|
||||
time.Sleep(time.Second * 2)
|
||||
|
||||
// 错误的情况重新注册一次
|
||||
if err != nil {
|
||||
var isRegisterErr bool
|
||||
for _, svc := range c.services {
|
||||
err := c.client.Agent().ServiceRegister(svc)
|
||||
if err != nil {
|
||||
defaultLog.Errorf("retry register service err: %s: %s", svc.Name, err)
|
||||
isRegisterErr = true
|
||||
break
|
||||
} else {
|
||||
defaultLog.Infof("retry register service ok: %s", svc.Name)
|
||||
}
|
||||
}
|
||||
|
||||
if isRegisterErr {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
wlog.isWatchErr = false
|
||||
}
|
||||
//fmt.Println("watch", u, raw)
|
||||
}
|
||||
|
||||
err = wp.RunWithClientAndHclog(c.client, wlog)
|
||||
if err != nil {
|
||||
log.Printf("watch err: %s", err)
|
||||
}
|
||||
isFirst = false
|
||||
|
||||
_, meta, err = c.client.Catalog().Nodes(&api.QueryOptions{
|
||||
WaitIndex: lastIndex,
|
||||
//WaitTime: time.Second * 5,
|
||||
})
|
||||
if err != nil {
|
||||
defaultLog.Errorf("health check err: %s", err)
|
||||
continue
|
||||
}
|
||||
lastIndex = meta.LastIndex
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Consul) Client() *api.Client {
|
||||
@@ -178,13 +254,27 @@ func (c *Consul) Client() *api.Client {
|
||||
}
|
||||
|
||||
func GrpcUrl(serviceName string, conf *myconf.Config) string {
|
||||
return GrpcUrlWithTag("", mygrpc.ServicePrefix+serviceName, conf)
|
||||
return GrpcUrlWithTag("", serviceName, conf)
|
||||
}
|
||||
|
||||
func GrpcUrlWithTag(tag string, serviceName string, conf *myconf.Config) string {
|
||||
return GrpcUrlWithTagByConfig(tag, serviceName, &ClientConfig{
|
||||
Address: conf.GetString("addr"),
|
||||
Token: conf.GetString("token"),
|
||||
Username: conf.GetString("username"),
|
||||
Password: conf.GetString("password"),
|
||||
AutoGrpcPrefix: true,
|
||||
})
|
||||
}
|
||||
|
||||
func GrpcUrlWithTagByConfig(tag string, serviceName string, conf *ClientConfig) string {
|
||||
if conf.AutoGrpcPrefix {
|
||||
serviceName = mygrpc.ServicePrefix + serviceName
|
||||
}
|
||||
|
||||
u := &url.URL{
|
||||
Scheme: schemeName,
|
||||
Host: conf.GetString("addr"),
|
||||
Host: conf.Address,
|
||||
Path: serviceName,
|
||||
}
|
||||
|
||||
@@ -195,44 +285,19 @@ func GrpcUrlWithTag(tag string, serviceName string, conf *myconf.Config) string
|
||||
query := u.Query()
|
||||
query.Set("healthy", "true")
|
||||
|
||||
if v := conf.GetString("token"); v != "" {
|
||||
query.Set("token", v)
|
||||
if conf.Token != "" {
|
||||
query.Set("token", conf.Token)
|
||||
}
|
||||
|
||||
if tag != "" {
|
||||
query.Set("tag", tag)
|
||||
}
|
||||
|
||||
username := conf.GetString("username")
|
||||
password := conf.GetString("password")
|
||||
|
||||
if username != "" && password != "" {
|
||||
u.User = url.UserPassword(username, password)
|
||||
if conf.Username != "" && conf.Password != "" {
|
||||
u.User = url.UserPassword(conf.Username, conf.Password)
|
||||
}
|
||||
|
||||
u.RawQuery = query.Encode()
|
||||
|
||||
return u.String()
|
||||
}
|
||||
|
||||
type watchLogger struct {
|
||||
hclog.Logger
|
||||
isWatchErr bool
|
||||
}
|
||||
|
||||
func newWatchLogger() *watchLogger {
|
||||
return &watchLogger{Logger: hclog.New(&hclog.LoggerOptions{
|
||||
Name: "watch",
|
||||
Output: os.Stdout,
|
||||
})}
|
||||
}
|
||||
|
||||
func (l *watchLogger) Error(msg string, args ...interface{}) {
|
||||
l.isWatchErr = true
|
||||
log.Printf("is watch err: %s", msg)
|
||||
l.Logger.Error(msg, args...)
|
||||
}
|
||||
|
||||
func (l *watchLogger) Named(name string) hclog.Logger {
|
||||
return l
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user