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.
 
 
 

193 lines
4.8 KiB

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 &registry.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"`
}