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
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 ®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"` |
|
}
|
|
|