Prometheus源码学习(4) 通过2.24对实例化Discoverer代码的改进学习依赖倒置

news/2024/5/19 0:03:36 标签: prometheus, go, 设计模式

前面读的是 2.19.2 版本的代码,最新更新了 2.24.1,发现在实例化 Discoverer 时改进了设计。这个改变是在 2.21 做出的。

2.19.2 中的实现方式

  1. 在 discovery.Manager 结构体中包含了一个 *provider 的 slice
go">type Manager struct {
	...
	// providers keeps track of SD providers.
	providers []*provider
    ...
}
  1. provider 接口提包含了一个 Discoverer 接口类型的字段
go">// provider holds a Discoverer instance, its configuration and its subscribers.
type provider struct {
	name   string
	d      Discoverer
	subs   []string
	config interface{}
}
  1. 实例化 Discoverer 是在 *Manager 的 registerProviders() 方法中实现的,每个 job 有一套 Provider

方法签名为

go">func (m *Manager) registerProviders(cfg sd_config.ServiceDiscoveryConfig, setName string) int 

参数 cfg 是解析配置文件参数得到的各种 SD 类型

go">// 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 的方法

go">    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
	}
  1. 遍历配置文件配置的每种服务发现的类型,调用这种类型的实例化方法(如 dns.NewDiscovery())来初始化这种SD。
go">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 中的实现方式

  1. Manager 包装 provider 包装 Discoverer 没有变化
  2. *Manager.registerProviders() 方法的参数变为了 Config 切片,Config 是接口类型,其中要求实现类要有 NewDiscoverer(DiscovererOptions) (Discoverer, error) 方法。
go">// 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() 方法

go">       d, err := cfg.NewDiscoverer(DiscovererOptions{
			Logger: log.With(m.logger, "discovery", typ),
		})

遍历也简化了

go">    for _, cfg := range cfgs {
		add(cfg)
	}
  1. 在 main() 里依赖了 /discovery/install,这个新增的 install 包中导入具体的 SD 作为依赖
go">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
)
  1. 每一个具体的 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实例

go">// 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 的导入部分变得简洁。

之前的依赖顺序

main
discovery
consul
file
ec2
kubernetes

如果添加一个 SD,需要添加 discovery/config/config.go 和 discovery/manager.go 中的依赖,并且要添加 *Manager.registerProviders() 的代码


现在的依赖顺序

main
install
consul
file
ec2
kubernetes
discovery

如果添加一个 SD,仅需要添加 discovery/install/install.go,删除了 discovery/config/ 目录


http://www.niftyadmin.cn/n/1531802.html

相关文章

类Hashtable(集合)

概述 java.util.Hashtable<K,V>集合 implements Map<K,V>接口Hashtable:底层也是一个哈希表,是一个线程安全的集合,是单线程集合,速度慢HashMap:底层是一个哈希表,是一个线程不安全的集合,是多线程的集合,速度快HashMap集合(之前学的所有的集合):可以存储null值,nu…

第一次开源项目代码贡献 - prometheus-nginxlog-exporter

2021年2月26日&#xff0c;我的第一次开源项目代码贡献被合并了。 这完成了我一个宿愿。几年前老罗向 OpenSSL 捐献手机发布会门票款&#xff0c;我在上班的地铁上看到《隐形战友》这篇文章&#xff0c;有点激动&#xff0c;许下一个心愿&#xff1a;此生哪怕为开源项目贡献一…

Prometheus源码学习(5) notifier

文章目录notifier 模块Alert 结构体Manager 结构体Manager.Run()Manager.sendAll()习得notifier 模块 notifier 模块是用于向 Alertmanager 发送告警通知的。 主要的结构体包括&#xff1a; AlertManagerOptionsalertMetricsalertmanagerLabelsalertmanagerSet Alert 结构体…

c#.net执行应用程序有两种方法

在asp.net中执行应用程序有两种方法&#xff1a;1、调用win32函数ShellExecute。2、用.NET Framework中的Process类。下面我分别用这两种方法执行Windows中的记事本程序notepad.exe。新建一个ASP.Net页面Default.aspx&#xff0c;在上面放一个按钮&#xff0c;进入Default.aspx…

Prometheus源码学习(6) labels

github.com/prometheus/prometheus/pkg/labels 时间序列由一组 Label 唯一标识。labels包中是对 Label 和 Labels 对象的定义和方法。还有一个构建 Labels 的 Builder 对象。 一个标签(Label)就是一个字符串键值对 // Label is a key/value pair of strings. type Label str…

Prometheus源码学习(7) targetgroup

targroup 是抓取目标 // Group is a set of targets with a common label set(production , test, staging etc.). // Group 是一组目标的集合&#xff0c;这组目标有一个共同的标签集。 type Group struct {// Targets is a list of targets identified by a label set. Each…

goroutine生命周期管理-errgroup和run

Go 语言有提供了多个包来将多个 goroutine 的生命周期编组管理。最简单的是标准库的 sync .WaitGroup&#xff0c;应用比较普遍的是 google 的 errgroup&#xff0c;Prometheus 用的是 oklog 的 run。下面学习后两个包的用法。 errgroup errgroup 为作为一个任务中的多个子任…

Prometheus源码学习(8) scrape总体流程

1. main 函数中初始化 scrapeManager 实例 // 初始化 scrapeManager&#xff0c;fanout Storage 是一个读写多个底层存储的代理 scrapeManager scrape.NewManager(log.With(logger, "component", "scrape manager"), fanoutStorage)fanoutStorage 是读写…