finclip-app-manager/infrastructure/consul/consul.go

221 lines
4.8 KiB
Go
Raw Normal View History

2023-10-31 14:07:26 +08:00
package consul
import (
"encoding/json"
"finclip-app-manager/infrastructure/config"
"fmt"
"net"
"net/http"
"strconv"
"time"
consulapi "github.com/hashicorp/consul/api"
"github.com/hashicorp/consul/api/watch"
uuid "github.com/satori/go.uuid"
)
var (
mopConsul *MopConsul
)
type MopConsul struct {
CheckPort string
NodeId string
ConsulClient *consulapi.Client
IsRegSucc bool
IsStop bool
}
func InitConsul() {
mopConsul = NewMopConsul()
mopConsul.Init()
}
func GetConsulInstance() *MopConsul {
return mopConsul
}
func NewMopConsul() *MopConsul {
var m MopConsul
m.CheckPort = "9091"
m.NodeId = uuid.NewV4().String()
m.IsRegSucc = false
m.IsStop = false
var err error
cfg := consulapi.DefaultConfig()
cfg.Address = config.GetConfig().ConsulAddr
m.ConsulClient, err = consulapi.NewClient(cfg)
if err != nil {
panic(err)
}
return &m
}
func (m *MopConsul) Init() {
m.InitCheck() //初始化check接口
time.Sleep(1 * time.Second)
m.RegisterServer() //注册服务
go m.WatchService() //服务是否发生change
go m.SyncServiceHealth(60) //主动拉取健康状态
}
func (m *MopConsul) InitCheck() {
http.HandleFunc("/"+m.NodeId+"/check", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("success"))
})
go http.ListenAndServe(":"+m.CheckPort, nil)
}
func (m *MopConsul) GetLocalIP() string {
addrs, err := net.InterfaceAddrs()
if err != nil {
return ""
}
for _, address := range addrs {
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
return ipnet.IP.String()
}
}
}
return ""
}
func (m *MopConsul) RegisterServer() {
if m.IsStop {
return
}
registration := new(consulapi.AgentServiceRegistration)
registration.ID = m.NodeId
registration.Name = config.GetConfig().ServerName
registration.Port, _ = strconv.Atoi(config.GetConfig().GrpcPort)
registration.Tags = []string{config.GetConfig().ConsulTag}
registration.Address = m.GetLocalIP()
fmt.Println("localIP=" + registration.Address)
registration.Check = &consulapi.AgentServiceCheck{
CheckID: m.NodeId,
HTTP: "http://" + registration.Address + ":" + m.CheckPort + "/" + m.NodeId + "/check",
Timeout: "5s",
Interval: "30s",
DeregisterCriticalServiceAfter: "35s", //check失败后35秒删除本服务
}
err := m.ConsulClient.Agent().ServiceRegister(registration)
if err != nil {
fmt.Errorf("ServiceRegister err:" + err.Error())
if m.IsRegSucc == false {
panic(err)
}
}
m.IsRegSucc = true
fmt.Println("Consul RegisterServer success")
}
func (m *MopConsul) DisRegisterServer() {
m.IsStop = true
err := m.ConsulClient.Agent().ServiceDeregister(m.NodeId)
if err != nil {
fmt.Errorf("ServiceDeregister service:%s err:%s\n", m.NodeId, err.Error())
} else {
fmt.Printf("ServiceDeregister service:%s succ\n", m.NodeId)
}
}
func (m *MopConsul) WatchService() {
var notifyCh = make(chan struct{})
watchContent := `{"type":"service", "service":"` + config.GetConfig().ServerName + `", "tag":"` + config.GetConfig().ConsulTag + `"}`
plan := m.WatchParse(watchContent)
plan.Handler = func(idx uint64, raw interface{}) {
fmt.Println("service change...")
if raw == nil {
return // ignore
}
fmt.Println("do something...")
notifyCh <- struct{}{}
}
go func() {
if err := plan.Run(config.GetConfig().ConsulAddr); err != nil {
panic(err)
}
}()
defer plan.Stop()
for {
select {
case <-notifyCh:
m.RegisterAgain()
}
}
}
func (m *MopConsul) SyncServiceHealth(gap int) {
for {
if m.IsStop {
break
}
time.Sleep(time.Duration(gap) * time.Second)
isHealth := false
services, _, err := m.ConsulClient.Health().Service(config.GetConfig().ServerName, config.GetConfig().ConsulTag, true, &consulapi.QueryOptions{WaitIndex: 0})
if err != nil {
fmt.Errorf("error retrieving instances from Consul: %s", err.Error())
}
for _, s := range services {
if s.Service.ID == m.NodeId {
isHealth = true
break
}
}
if !isHealth {
m.RegisterServer()
}
}
}
func (m *MopConsul) WatchParse(q string) *watch.Plan {
var params map[string]interface{}
if err := json.Unmarshal([]byte(q), &params); err != nil {
fmt.Errorf("Unmarshal err:" + err.Error())
return nil
}
plan, err := watch.Parse(params)
if err != nil {
panic(err)
}
return plan
}
func (m *MopConsul) RegisterAgain() {
if !m.CheckServiceIsOK() {
m.RegisterServer()
}
}
func (m *MopConsul) CheckServiceIsOK() bool {
services, _ := m.ConsulClient.Agent().Services()
isOK := false
if len(services) > 0 {
if value, ok := services[m.NodeId]; ok {
if value.Service == config.GetConfig().ServerName {
if value.Weights.Passing == 1 {
isOK = true
}
}
}
}
return isOK
}