前面读的是 2.19.2 版本的代码,最新更新了 2.24.1,发现在实例化 Discoverer 时改进了设计。这个改变是在 2.21 做出的。
2.19.2 中的实现方式
- 在 discovery.Manager 结构体中包含了一个 *provider 的 slice
type Manager struct {
...
// providers keeps track of SD providers.
providers []*provider
...
}
- provider 接口提包含了一个 Discoverer 接口类型的字段
// provider holds a Discoverer instance, its configuration and its subscribers.
type provider struct {
name string
d Discoverer
subs []string
config interface{}
}
- 实例化 Discoverer 是在 *Manager 的 registerProviders() 方法中实现的,每个 job 有一套 Provider
方法签名为
func (m *Manager) registerProviders(cfg sd_config.ServiceDiscoveryConfig, setName string) int
参数 cfg 是解析配置文件参数得到的各种 SD 类型
// ServiceDiscoveryConfig configures lists of different service discovery mechanisms.
type ServiceDiscoveryConfig struct {
// List of labeled target groups for this job.
StaticConfigs []*targetgroup.Group `yaml:"static_configs,omitempty"`
// List of DNS service discovery configurations.
DNSSDConfigs []*dns.SDConfig `yaml:"dns_sd_configs,omitempty"`
// List of file service discovery configurations.
FileSDConfigs []*file.SDConfig `yaml:"file_sd_configs,omitempty"`
// List of Consul service discovery configurations.
ConsulSDConfigs []*consul.SDConfig `yaml:"consul_sd_configs,omitempty"`
// List of DigitalOcean service discovery configurations.
DigitalOceanSDConfigs []*digitalocean.SDConfig `yaml:"digitalocean_sd_configs,omitempty"`
// List of Docker Swarm service discovery configurations.
DockerSwarmSDConfigs []*dockerswarm.SDConfig `yaml:"dockerswarm_sd_configs,omitempty"`
// List of Serverset service discovery configurations.
ServersetSDConfigs []*zookeeper.ServersetSDConfig `yaml:"serverset_sd_configs,omitempty"`
// NerveSDConfigs is a list of Nerve service discovery configurations.
NerveSDConfigs []*zookeeper.NerveSDConfig `yaml:"nerve_sd_configs,omitempty"`
// MarathonSDConfigs is a list of Marathon service discovery configurations.
MarathonSDConfigs []*marathon.SDConfig `yaml:"marathon_sd_configs,omitempty"`
// List of Kubernetes service discovery configurations.
KubernetesSDConfigs []*kubernetes.SDConfig `yaml:"kubernetes_sd_configs,omitempty"`
// List of GCE service discovery configurations.
GCESDConfigs []*gce.SDConfig `yaml:"gce_sd_configs,omitempty"`
// List of EC2 service discovery configurations.
EC2SDConfigs []*ec2.SDConfig `yaml:"ec2_sd_configs,omitempty"`
// List of OpenStack service discovery configurations.
OpenstackSDConfigs []*openstack.SDConfig `yaml:"openstack_sd_configs,omitempty"`
// List of Azure service discovery configurations.
AzureSDConfigs []*azure.SDConfig `yaml:"azure_sd_configs,omitempty"`
// List of Triton service discovery configurations.
TritonSDConfigs []*triton.SDConfig `yaml:"triton_sd_configs,omitempty"`
}
方法内部首先声名一个添加 Discoverer 的闭包函数 add(),闭包函数的参数有一个实例化 Discoverer 的方法
add := func(cfg interface{}, newDiscoverer func() (Discoverer, error)) {
t := reflect.TypeOf(cfg).String()
// 已有的 provider 如果有多个 job 配置了它就把 job_name 追加到 这个provider 的 subs 中。
// 否则就新建 provider
for _, p := range m.providers {
if reflect.DeepEqual(cfg, p.config) {
p.subs = append(p.subs, setName)
level.Info(m.logger).Log("msg", "append subscribers", "sub", p.subs)
added = true
return
}
}
d, err := newDiscoverer()
if err != nil {
level.Error(m.logger).Log("msg", "Cannot create service discovery", "err", err, "type", t)
failedCount++
return
}
provider := provider{
name: fmt.Sprintf("%s/%d", t, len(m.providers)),
d: d,
config: cfg,
subs: []string{setName},
}
m.providers = append(m.providers, &provider)
added = true
}
- 遍历配置文件配置的每种服务发现的类型,调用这种类型的实例化方法(如 dns.NewDiscovery())来初始化这种SD。
for _, c := range cfg.DNSSDConfigs {
add(c, func() (Discoverer, error) {
return dns.NewDiscovery(*c, log.With(m.logger, "discovery", "dns")), nil
})
}
for _, c := range cfg.FileSDConfigs {
add(c, func() (Discoverer, error) {
return file.NewDiscovery(c, log.With(m.logger, "discovery", "file")), nil
})
}
for _, c := range cfg.ConsulSDConfigs {
add(c, func() (Discoverer, error) {
return consul.NewDiscovery(c, log.With(m.logger, "discovery", "consul"))
})
}
...
这种模式要在 manager.go 文件中导入每一种具体的服务发现器,也就是要在抽象层依赖各个实现。
2.24.1 中的实现方式
- Manager 包装 provider 包装 Discoverer 没有变化
- *Manager.registerProviders() 方法的参数变为了 Config 切片,Config 是接口类型,其中要求实现类要有 NewDiscoverer(DiscovererOptions) (Discoverer, error) 方法。
// Configs is a slice of Config values that uses custom YAML marshaling and unmarshaling
// to represent itself as a mapping of the Config values grouped by their types.
type Configs []Config
// A Config provides the configuration and constructor for a Discoverer.
type Config interface {
// Name returns the name of the discovery mechanism.
Name() string
// NewDiscoverer returns a Discoverer for the Config
// with the given DiscovererOptions.
NewDiscoverer(DiscovererOptions) (Discoverer, error)
}
*Manager.registerProviders() 里面的 add 闭包函数的初始化 Discoverer 方法是调用接口要求的 NewDiscoverer() 方法
d, err := cfg.NewDiscoverer(DiscovererOptions{
Logger: log.With(m.logger, "discovery", typ),
})
遍历也简化了
for _, cfg := range cfgs {
add(cfg)
}
- 在 main() 里依赖了 /discovery/install,这个新增的 install 包中导入具体的 SD 作为依赖
package install
import (
_ "github.com/prometheus/prometheus/discovery/azure" // register azure
_ "github.com/prometheus/prometheus/discovery/consul" // register consul
_ "github.com/prometheus/prometheus/discovery/digitalocean" // register digitalocean
_ "github.com/prometheus/prometheus/discovery/dns" // register dns
_ "github.com/prometheus/prometheus/discovery/dockerswarm" // register dockerswarm
_ "github.com/prometheus/prometheus/discovery/ec2" // register ec2
_ "github.com/prometheus/prometheus/discovery/eureka" // register eureka
_ "github.com/prometheus/prometheus/discovery/file" // register file
_ "github.com/prometheus/prometheus/discovery/gce" // register gce
_ "github.com/prometheus/prometheus/discovery/hetzner" // register hetzner
_ "github.com/prometheus/prometheus/discovery/kubernetes" // register kubernetes
_ "github.com/prometheus/prometheus/discovery/marathon" // register marathon
_ "github.com/prometheus/prometheus/discovery/openstack" // register openstack
_ "github.com/prometheus/prometheus/discovery/triton" // register triton
_ "github.com/prometheus/prometheus/discovery/zookeeper" // register zookeeper
)
- 每一个具体的 SD 依赖 /discovery/dicovery,实现接口的 NewDiscoverer(opts discovery.DiscovererOptions) (discovery.Discoverer, error) 方法
// NewDiscoverer returns a Discoverer for the Config.
func (c *SDConfig) NewDiscoverer(opts discovery.DiscovererOptions) (discovery.Discoverer, error) {
return NewDiscovery(c, opts.Logger), nil
}
返回的是调用本 SD 自己的 NewDiscovery() 函数得到的具体的SD实例
// NewDiscovery returns a new file discovery for the given paths.
func NewDiscovery(conf *SDConfig, logger log.Logger) *Discovery {
if logger == nil {
logger = log.NewNopLogger()
}
disc := &Discovery{
paths: conf.Files,
interval: time.Duration(conf.RefreshInterval),
timestamps: make(map[string]float64),
logger: logger,
}
fileSDTimeStamp.addDiscoverer(disc)
return disc
}
模式学习
这样就实现了由抽象依赖实现翻转为实现依赖抽象。
install 是一个中介包,main 依赖它,它的内部导入各个具体的 SD 实现包,这样集中了多个具体实现的导入位置便于修改,也使 main 的导入部分变得简洁。
之前的依赖顺序
如果添加一个 SD,需要添加 discovery/config/config.go 和 discovery/manager.go 中的依赖,并且要添加 *Manager.registerProviders() 的代码
现在的依赖顺序
如果添加一个 SD,仅需要添加 discovery/install/install.go,删除了 discovery/config/ 目录