You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
194 lines
4.8 KiB
194 lines
4.8 KiB
2 years ago
|
package discovery
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"net/url"
|
||
|
"os"
|
||
|
"strconv"
|
||
|
"time"
|
||
|
|
||
|
"github.com/pkg/errors"
|
||
|
|
||
|
"github.com/go-kratos/kratos/v2/registry"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
ErrDuplication = errors.New("register failed: instance duplicated: ")
|
||
|
ErrServerError = errors.New("server error")
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
// Discovery server resource uri
|
||
|
_registerURL = "http://%s/discovery/register"
|
||
|
//_setURL = "http://%s/discovery/set"
|
||
|
_cancelURL = "http://%s/discovery/cancel"
|
||
|
_renewURL = "http://%s/discovery/renew"
|
||
|
_pollURL = "http://%s/discovery/polls"
|
||
|
|
||
|
// Discovery server error codes
|
||
|
_codeOK = 0
|
||
|
_codeNotFound = -404
|
||
|
_codeNotModified = -304
|
||
|
//_SERVER_ERROR = -500
|
||
|
|
||
|
// _registerGap is the gap to renew instance registration.
|
||
|
_registerGap = 30 * time.Second
|
||
|
_statusUP = "1"
|
||
|
_discoveryAppID = "infra.discovery"
|
||
|
)
|
||
|
|
||
|
// Config Discovery configures.
|
||
|
type Config struct {
|
||
|
Nodes []string
|
||
|
Region string
|
||
|
Zone string
|
||
|
Env string
|
||
|
Host string
|
||
|
}
|
||
|
|
||
|
func fixConfig(c *Config) error {
|
||
|
if c.Host == "" {
|
||
|
c.Host, _ = os.Hostname()
|
||
|
}
|
||
|
if len(c.Nodes) == 0 || c.Region == "" || c.Zone == "" || c.Env == "" || c.Host == "" {
|
||
|
return fmt.Errorf(
|
||
|
"invalid Discovery config nodes:%+v region:%s zone:%s deployEnv:%s host:%s",
|
||
|
c.Nodes,
|
||
|
c.Region,
|
||
|
c.Zone,
|
||
|
c.Env,
|
||
|
c.Host,
|
||
|
)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// discoveryInstance represents a server the client connects to.
|
||
|
type discoveryInstance struct {
|
||
|
Region string `json:"region"` // Region is region.
|
||
|
Zone string `json:"zone"` // Zone is IDC.
|
||
|
Env string `json:"env"` // Env prod/pre/uat/fat1
|
||
|
AppID string `json:"appid"` // AppID is mapping service-tree appId.
|
||
|
Hostname string `json:"hostname"` // Hostname is hostname from docker
|
||
|
Addrs []string `json:"addrs"` // Addrs is the address of app instance format: scheme://host
|
||
|
Version string `json:"version"` // Version is publishing version.
|
||
|
LastTs int64 `json:"latest_timestamp"` // LastTs is instance latest updated timestamp
|
||
|
// Metadata is the information associated with Addr, which may be used to make load balancing decision.
|
||
|
Metadata map[string]string `json:"metadata"`
|
||
|
Status int64 `json:"status"` // Status instance status, eg: 1UP 2Waiting
|
||
|
}
|
||
|
|
||
|
const _reservedInstanceIDKey = "kratos.v2.serviceinstance.id"
|
||
|
|
||
|
// fromServerInstance convert registry.ServiceInstance into discoveryInstance
|
||
|
func fromServerInstance(ins *registry.ServiceInstance, config *Config) *discoveryInstance {
|
||
|
if ins == nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
metadata := ins.Metadata
|
||
|
if ins.Metadata == nil {
|
||
|
metadata = make(map[string]string, 8)
|
||
|
}
|
||
|
metadata[_reservedInstanceIDKey] = ins.ID
|
||
|
|
||
|
return &discoveryInstance{
|
||
|
Region: config.Region,
|
||
|
Zone: config.Zone,
|
||
|
Env: config.Env,
|
||
|
AppID: ins.Name,
|
||
|
Hostname: config.Host,
|
||
|
Addrs: ins.Endpoints,
|
||
|
Version: ins.Version,
|
||
|
LastTs: time.Now().Unix(),
|
||
|
Metadata: metadata,
|
||
|
Status: 1,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// toServiceInstance convert discoveryInstance into registry.ServiceInstance
|
||
|
func toServiceInstance(ins *discoveryInstance) *registry.ServiceInstance {
|
||
|
if ins == nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
md := map[string]string{
|
||
|
"region": ins.Region,
|
||
|
"zone": ins.Zone,
|
||
|
"lastTs": strconv.Itoa(int(ins.LastTs)),
|
||
|
"env": ins.Env,
|
||
|
"hostname": ins.Hostname,
|
||
|
}
|
||
|
|
||
|
if len(ins.Metadata) != 0 {
|
||
|
for k, v := range ins.Metadata {
|
||
|
md[k] = v
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ®istry.ServiceInstance{
|
||
|
ID: ins.Metadata[_reservedInstanceIDKey],
|
||
|
Name: ins.AppID,
|
||
|
Version: ins.Version,
|
||
|
Metadata: md,
|
||
|
Endpoints: ins.Addrs,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// disInstancesInfo instance info.
|
||
|
type disInstancesInfo struct {
|
||
|
Instances map[string][]*discoveryInstance `json:"instances"`
|
||
|
LastTs int64 `json:"latest_timestamp"`
|
||
|
Scheduler *scheduler `json:"scheduler"`
|
||
|
}
|
||
|
|
||
|
// scheduler scheduler.
|
||
|
type scheduler struct {
|
||
|
Clients map[string]*zoneStrategy `json:"clients"`
|
||
|
}
|
||
|
|
||
|
// zoneStrategy is the scheduling strategy of all zones
|
||
|
type zoneStrategy struct {
|
||
|
Zones map[string]*strategy `json:"zones"`
|
||
|
}
|
||
|
|
||
|
// strategy is zone scheduling strategy.
|
||
|
type strategy struct {
|
||
|
Weight int64 `json:"weight"`
|
||
|
}
|
||
|
|
||
|
const (
|
||
|
_paramKeyRegion = "region"
|
||
|
_paramKeyZone = "zone"
|
||
|
_paramKeyEnv = "env"
|
||
|
_paramKeyHostname = "hostname"
|
||
|
_paramKeyAppID = "appid"
|
||
|
_paramKeyAddrs = "addrs"
|
||
|
_paramKeyVersion = "version"
|
||
|
_paramKeyStatus = "status"
|
||
|
_paramKeyMetadata = "metadata"
|
||
|
)
|
||
|
|
||
|
func newParams(c *Config) url.Values {
|
||
|
p := make(url.Values, 8)
|
||
|
if c == nil {
|
||
|
return p
|
||
|
}
|
||
|
|
||
|
p.Set(_paramKeyRegion, c.Region)
|
||
|
p.Set(_paramKeyZone, c.Zone)
|
||
|
p.Set(_paramKeyEnv, c.Env)
|
||
|
p.Set(_paramKeyHostname, c.Host)
|
||
|
return p
|
||
|
}
|
||
|
|
||
|
type discoveryCommonResp struct {
|
||
|
Code int `json:"code"`
|
||
|
Message string `json:"message"`
|
||
|
}
|
||
|
|
||
|
type discoveryPollsResp struct {
|
||
|
Code int `json:"code"`
|
||
|
Data map[string]*disInstancesInfo `json:"data"`
|
||
|
}
|