xdiscovery

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 23, 2020 License: Apache-2.0 Imports: 9 Imported by: 0

README

支持特性

  1. 双向健康检查(本基础库会主动和consul保活; 且可配置服务健康检查的tcp ip:port, consul 会主动来检测服务健康状态,默认使用服务注册的ip和端口)
  2. 本地缓存服务列表,并实时更新服务变化(通过服务版本号O(1)高效判断或注册变化回调通知函数)
  3. 支持服务 MetaData 自定义数据携带(服务注册时设置,发现时也会带上)
  4. 支持服务权重设置,可以自己实现按权负载均衡
  5. consul 集群故障时不影响现有发现的服务列表,且会在集群恢复后主动同步最近状态
  6. 支持 consul,eds,etcd 等多种注册中心

自我保护模式和注册规范说明

使用方法

// 初始化
d, err := factory.NewDiscovery(factory.KernelConsul, factory.Opts{
    ConsulOpts: consul.Opts{ // 只有在使用 KernelConsul 时才需要
        Address: "http://127.0.0.1:8500", // consul 地址(必填)
    },
    Degrade: xdiscovery.DegradeOpts{
        Threshold: 0.5, // 阈值, consul 当前获取的列表数/以前的列表 低于这个值进入自我保护
        Flag:      0,   // 0-开启自我保护; 1-关闭自我保护,使用注册中心的数据, 默认 0
    },
    InitHistoryEndpoints: initHistoryEndpoints, // 每个服务对应的初始化数据(进程重启时从其他地方恢复,map 的 key 是服务名)
})

svc := &xdiscovery.Service{
    Name: // ops 应用名(prd/pre环境使用服务名区分具体请遵循服务注册规范)
    Address: // 本机ip
    Port: // 服务端口
    Weight: // 100, 节点权重
    Meta: // 跨集群(或者k8s内的流量灰度)流量权重设置,自我保护健康检查等信息,具体需遵守注册规范
}
p, err := d.Register(svc)
// 注销服务
err = p.Deregister()
// 通过负载均衡每次选择下一个节点
lb, err := loadbalance.NewLoadBalance(d) // 只需要全局初始化一次

endpoint, err = lb.Next(service) // 每次请求都调用

服务名规范

服务名必须只包含字符[0-9,a-z,A-Z,-]

自定义负载均衡器获取服务的两种方式(有自己定制负载均衡需求的才需要使用)

// ### 注意: 自定义负载均衡为了提高性能做了 zero copy ,所有返回的服务列表的指针(以及map,slice等)数据都不要在外部直接修改!否则会导致内部数据错误
// 获取服务列表(会自动 watch 监听变化),可以通过比较版本号来确定服务列表是否被更新了
// 绑定负载均衡器
checkChange := xdiscovery.NewCheckChange(func(list *xdiscovery.ServiceList) (interface{}, error){
    // lb := newLB(list.Services)
    // return lb,nil

    // 这里的代码会在调用 checkChange.GetValue 时节点发生变更时才会回调,用于新建或更新负载均衡对象
})
// 每次请求都调用(效率高,有缓存),并发安全
serviceList, err := d.GetServers(service)
lb, updated, err := checkChange.GetValue(serviceList)
// lb.Next() // 负载均衡器选择下一个节点
/*======*/

Documentation

Overview

Copyright 2020 SunJun <i@sjis.me>

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Copyright 2020 SunJun <i@sjis.me>

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Copyright 2020 SunJun <i@sjis.me>

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Copyright 2020 SunJun <i@sjis.me>

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Copyright 2020 SunJun <i@sjis.me>

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Copyright 2020 SunJun <i@sjis.me>

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Copyright 2020 SunJun <i@sjis.me>

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Copyright 2020 SunJun <i@sjis.me>

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Copyright 2020 SunJun <i@sjis.me>

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Copyright 2020 SunJun <i@sjis.me>

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Copyright 2020 SunJun <i@sjis.me>

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Copyright 2020 SunJun <i@sjis.me>

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Copyright 2020 SunJun <i@sjis.me>

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Index

Constants

View Source
const (
	HealthCheckMetaKey = "health-check" // HealthCheckMetaKey 健康检查信息 meta 字段key
	RegTimeMetaKey     = "reg-time"     // RegTimeMetaKey 服务注册时间 meta 字段key
)

Variables

View Source
var (
	RegexpService     = regexp.MustCompile("^([0-9,a-z,A-Z,-]+\\.)*([0-9,a-z,A-Z,-]+)\\.service\\.discovery$")
	RegexpServiceName = regexp.MustCompile("^([0-9,a-z,A-Z,-]+)$")
)
View Source
var ErrNotExistOnRecover = errors.New("not exis on recover")

ErrNotExistOnRecover 恢复版本中不存在这个服务

View Source
var (
	ErrServiceNameInvalid = errors.New("service name invalid") // ErrServiceNameInvalid 服务名格式错误
)
View Source
var (
	// Log 全局日志对象
	Log = Logger(logrus.NewEntry(logrus.StandardLogger()))
)
View Source
var WrapWithRecoverDiscovery = func(d Discovery) WrapRecover {
	return &WithRecoverDiscovery{
		Discovery:       d,
		watchedServices: make(map[string]struct{}),
	}
}

WrapWithRecoverDiscovery 包装 Discovery

Functions

func CheckServiceOrTagName

func CheckServiceOrTagName(name string) bool

CheckServiceOrTagName 检查服务名或者tag是否符合规范

func GetTagsAndServiceName

func GetTagsAndServiceName(serviceDomain string) (service string, tags []string, err error)

GetTagsAndServiceName 通过域名获取服务名和 tags

func SetLogger

func SetLogger(logger Logger)

SetLogger 设置全局日志对象

Types

type Action

type Action int

Action 变更事件

const (
	ActionAdd Action = iota + 1 // ActionAdd 增加节点
	ActionDel                   // ActionDel 减少节点
	ActionMod                   // ActionMod 修改节点
)

type Adapter

type Adapter interface {
	// Register 注册一个服务 并返回服务注册器
	Register(svc *Service, opts ...RegisterOpts) (Registry, error)
	// WatchList 监听一个服务变更一次性返回整个 list, 不区分 tag, 直接监听所有 tags
	WatchList(service string, onUpdateList OnUpdateList, opts ...WatchOption) (Watcher, error)
}

Adapter 注册中心适配器接口

type CheckChange

type CheckChange struct {
	// contains filtered or unexported fields
}

CheckChange 检查

func NewCheckChange

func NewCheckChange(newValue func(list *ServiceList) (interface{}, error), opts ...CheckChangeOpts) (*CheckChange, error)

NewCheckChange 新建服务变更检查器,用于GetServers() 返回的服务列表检查是否变更; newValue() 是在服务变更时新建的值,比如新的负载均衡器,注意可能存在newValue多次但只一个对象会被引用

func (*CheckChange) GetValue

func (c *CheckChange) GetValue(list *ServiceList) (value interface{}, updated bool, err error)

GetValue 根据服务是否变更返回最新的 value,以及是否发生了变更 注意: 启用 CheckChangeOpts.AsyncUpdate 时返回updated为true表示触发了value更新,而本次返回的value还是旧值

type CheckChangeOpts

type CheckChangeOpts struct {
	// AsyncUpdate newValue 是否异步执行,异步执行可能会让 GetValue 获取到旧的value值后才触发更新, 而同步执行会阻塞 GetValue,
	//				建议后端服务节点较多时开启 AsyncUpdate, 否则频繁的上下线节点会导致 GetValue 阻塞较多时间,
	//				超低频访问(几分钟才有一次访问的)的服务不建议开启 AsyncUpdate
	AsyncUpdate bool
}

CheckChangeOpts 节点变化配置检查

type DegradeOpts

type DegradeOpts struct {
	Threshold                 float64       // 自我保护阈值, consul 当前获取的列表数/以前的列表 低于这个值进入自我保护, hc 后节点和当前实时节点一致时推出
	PanicThreshold            float64       // 恐慌阈值, 在自我保护状态下, hc 后节点/以前的列表 低于这个值进入,当此比例大于 Threshold 后推出到自我保护状态
	ThresholdContrastInterval time.Duration // 和多久前对比计算,同时也是历史节点删除的窗口(默认 15m)
	EndpointsSaveInterval     time.Duration // 历史节点归档记录间隔(默认 1m)
	Flag                      int32         // 0-开启自我保护; 1-关闭自我保护,使用注册中心的数据, 默认 0
	HealthCheckInterval       time.Duration // 两次全局健康检查最少间隔时间,默认 30s
	PingTimeout               time.Duration // 健康检查超时时间,默认 500ms(如果注册中心返回了健康检查配置,则此配置无效)
}

DegradeOpts 降级配置

type Discovery

type Discovery interface {
	Adapter

	// Watch 监听一个服务变更, 不区分 tag, 直接监听所有 tags
	Watch(service string, onUpdate OnUpdate, opts ...WatchOption) (Watcher, error)

	// GetServers 按照这组服务节点列表和版本号, 请不要修改任何 *ServiceList 中的变量
	GetServers(service string, tags ...string) (*ServiceList, error)
	GetServersWithDC(service string, dc string, tags ...string) (*ServiceList, error)

	UpdateDegradeOpts(opts *DegradeOpts) error // UpdateDegradeOpts  动态更新降级配置, DegradeOpts 具体降级配置
	Shutdown() error                           // Shutdown 停止
}

Discovery 服务发现接口

type Event

type Event struct {
	Action  Action
	Service *Service
}

Event 服务事件

type HTTPCheck

type HTTPCheck struct {
	Path   string              // 路径
	Method string              // 方法
	Header map[string][]string // 请求头
}

HTTPCheck http 健康检查

type HealthCheck

type HealthCheck struct {
	Interval api.ReadableDuration `json:"interval,omitempty"` // 健康检查间隔时间
	Timeout  api.ReadableDuration `json:"timeout,omitempty"`  // 健康检查超时时间
	HTTP     string               `json:"http,omitempty"`     // http 健康检查完整路径 http://...
	Header   map[string][]string  `json:"header,omitempty"`   // http 健康检查带上的 Header
	Method   string               `json:"method,omitempty"`   // http 健康检查的 Method
	TCP      string               `json:"tcp,omitempty"`      // TCP 健康检查的 ip:port
}

HealthCheck 返回的健康检查

type Logger

type Logger interface {
	Debugf(format string, args ...interface{})
	Infof(format string, args ...interface{})
	Warnf(format string, args ...interface{})
	Errorf(format string, args ...interface{})

	Writer() *io.PipeWriter
}

Logger 日志

type OnUpdate

type OnUpdate func(Event) error

OnUpdate 请不要修改任何 Event 中的变量

type OnUpdateList

type OnUpdateList func(ServiceList) error

OnUpdateList 请不要修改任何 Service 中的变量

type RegisterOpts

type RegisterOpts struct {
	CheckTTL      time.Duration // 服务注册后和 agent 心跳间隔(注册中心异常后可检测到), 默认为 Discovery 中的配置
	CheckIP       string        // 健康检查 IP, 默认为注册的服务 IP
	CheckPort     int           // 健康检查 tcp 端口, 默认为注册的服务端口
	CheckInterval time.Duration // 健康检查间隔时间, 默认 5s
	CheckTimeout  time.Duration // 健康检查超时时间, 默认 2s
	CheckHTTP     *HTTPCheck    // http 健康检查,为空则使用 tcp 端口检查
}

RegisterOpts 服务注册配置

type Registry

type Registry interface {
	Deregister() error                               // Deregister 注销服务
	Update(svc *Service, opts ...RegisterOpts) error // Update 更新服务注册信息
}

Registry 服务注册后用于注销或更新

type RunMode

type RunMode int32

RunMode 发现库运行模式

const (
	RunModeNormal         RunMode = iota + 1 // RunModeNormal 正常的服务发现
	RunModeRecover                           // RunModeRecover 使用本地文件
	RunModeSelfProtection                    // RunModeSelfProtection 节点处于自我保护模式
	RunModeInit                              // RunModeInit 节点处于初始化数据
	RunModePanic                             // RunModePanic 节点处于恐慌状态
)

type Service

type Service struct {
	ID          string            `json:"id"`           // 节点全局唯一标识
	Name        string            `json:"name"`         // 服务名
	Address     string            `json:"address"`      // 节点地址
	Port        int               `json:"port"`         // 节点端口
	Tags        []string          `json:"tags"`         // 节点 tag 属性
	Weight      int               `json:"weight"`       // 节点权重
	Meta        map[string]string `json:"meta"`         // metadata
	HealthCheck *HealthCheck      `json:"health_check"` // 注册到 consul 中的健康检查(返回节点时才会存在,注册时填写无效)
}

Service 服务信息

func (*Service) Copy

func (svc *Service) Copy() *Service

Copy 复制 Service 对象

func (*Service) GetSelfProtectionID

func (svc *Service) GetSelfProtectionID() string

type ServiceList

type ServiceList struct {
	WatchVersion uint64  // watcher 版本号(递增)
	Version      uint64  // 服务列表版本号(递增),[0,maxUint32]表示不走降级,[maxUint32+1,maxUint64]表示降级列表,版本号有可能从高到底转换
	RunMode      RunMode // 运行模式,正常或者恢复到了历史版本,变更会刷新缓存(只在 wrap 被使用)
	Services     []*Service
}

ServiceList 服务节点列表

type WatchOption

type WatchOption struct {
	DC string
}

WatchOption watch配置

type Watcher

type Watcher interface {
	Stop() error
	WaitReady() error
}

Watcher 单个服务事件监听

type WithRecoverDiscovery

type WithRecoverDiscovery struct {
	Discovery
	// contains filtered or unexported fields
}

WithRecover 可以恢复历史版本的 discovery

func (*WithRecoverDiscovery) GetAllWatchedServices

func (d *WithRecoverDiscovery) GetAllWatchedServices() (map[string]ServiceList, error)

GetAllWatchedServices 获得所有通过 GetServers() watch 过的服务

func (*WithRecoverDiscovery) GetServers

func (d *WithRecoverDiscovery) GetServers(service string, tags ...string) (*ServiceList, error)

GetServers

func (*WithRecoverDiscovery) GetServersWithDC

func (d *WithRecoverDiscovery) GetServersWithDC(service string, dc string, tags ...string) (*ServiceList, error)

GetServersWithDC 不支持 tags

func (*WithRecoverDiscovery) SetRecover

func (d *WithRecoverDiscovery) SetRecover(srvs map[string]ServiceList)

SetRecover 设置返回的服务列表信息

type WrapRecover

type WrapRecover interface {
	Discovery
	// GetAllWatchedServices 获得所有通过 GetServers() watch 过的服务
	GetAllWatchedServices() (map[string]ServiceList, error)
	// SetRecover 设置返回的服务列表信息,srvs 为nil时会恢复到 RunModeNormal 状态,否则变成 RunModeRecover,不要修改ServiceList.Services(传入的是引用)
	SetRecover(srvs map[string]ServiceList)
}

WrapRecover 恢复历史版本包装器

Directories

Path Synopsis
eds
eds/mockv2
Package mock_v2 is a generated GoMock package.
Package mock_v2 is a generated GoMock package.
internal
wrr

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL