VictoriaMetrics/lib/promscrape/discovery/eureka/eureka.go
Aliaksandr Valialkin be6da5053f
lib/promscrape: optimize service discovery speed
- Return meta-labels for the discovered targets via promutils.Labels
  instead of map[string]string. This improves the speed of generating
  meta-labels for discovered targets by up to 5x.

- Remove memory allocations in hot paths during ScrapeWork generation.
  The ScrapeWork contains scrape settings for a single discovered target.
  This improves the service discovery speed by up to 2x.
2022-11-29 21:26:23 -08:00

153 lines
5.6 KiB
Go

package eureka
import (
"encoding/xml"
"flag"
"fmt"
"strconv"
"time"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promauth"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promscrape/discoveryutils"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/proxy"
)
// SDCheckInterval defines interval for targets refresh.
var SDCheckInterval = flag.Duration("promscrape.eurekaSDCheckInterval", 30*time.Second, "Interval for checking for changes in eureka. "+
"This works only if eureka_sd_configs is configured in '-promscrape.config' file. "+
"See https://docs.victoriametrics.com/sd_configs.html#eureka_sd_configs for details")
// SDConfig represents service discovery config for eureka.
//
// See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#eureka
type SDConfig struct {
Server string `yaml:"server,omitempty"`
HTTPClientConfig promauth.HTTPClientConfig `yaml:",inline"`
ProxyURL *proxy.URL `yaml:"proxy_url,omitempty"`
ProxyClientConfig promauth.ProxyClientConfig `yaml:",inline"`
// RefreshInterval time.Duration `yaml:"refresh_interval"`
// refresh_interval is obtained from `-promscrape.ec2SDCheckInterval` command-line option.
}
type applications struct {
Applications []Application `xml:"application"`
}
// Application - eureka application https://github.com/Netflix/eureka/wiki/Eureka-REST-operations/
type Application struct {
Name string `xml:"name"`
Instances []Instance `xml:"instance"`
}
// Port - eureka instance port.
type Port struct {
Port int `xml:",chardata"`
Enabled bool `xml:"enabled,attr"`
}
// Instance - eureka instance https://github.com/Netflix/eureka/wiki/Eureka-REST-operations
type Instance struct {
HostName string `xml:"hostName"`
HomePageURL string `xml:"homePageUrl"`
StatusPageURL string `xml:"statusPageUrl"`
HealthCheckURL string `xml:"healthCheckUrl"`
App string `xml:"app"`
IPAddr string `xml:"ipAddr"`
VipAddress string `xml:"vipAddress"`
SecureVipAddress string `xml:"secureVipAddress"`
Status string `xml:"status"`
Port Port `xml:"port"`
SecurePort Port `xml:"securePort"`
DataCenterInfo DataCenterInfo `xml:"dataCenterInfo"`
Metadata MetaData `xml:"metadata"`
CountryID int `xml:"countryId"`
InstanceID string `xml:"instanceId"`
}
// MetaData - eureka objects metadata.
type MetaData struct {
Items []Tag `xml:",any"`
}
// Tag - eureka metadata tag - list of k/v values.
type Tag struct {
XMLName xml.Name
Content string `xml:",innerxml"`
}
// DataCenterInfo -eureka datacentre metadata
type DataCenterInfo struct {
Name string `xml:"name"`
Metadata MetaData `xml:"metadata"`
}
// GetLabels returns Eureka labels according to sdc.
func (sdc *SDConfig) GetLabels(baseDir string) ([]*promutils.Labels, error) {
cfg, err := getAPIConfig(sdc, baseDir)
if err != nil {
return nil, fmt.Errorf("cannot get API config: %w", err)
}
data, err := getAPIResponse(cfg, "/apps")
if err != nil {
return nil, err
}
apps, err := parseAPIResponse(data)
if err != nil {
return nil, err
}
return addInstanceLabels(apps), nil
}
// MustStop stops further usage for sdc.
func (sdc *SDConfig) MustStop() {
configMap.Delete(sdc)
}
func addInstanceLabels(apps *applications) []*promutils.Labels {
var ms []*promutils.Labels
for _, app := range apps.Applications {
for _, instance := range app.Instances {
instancePort := 80
if instance.Port.Port != 0 {
instancePort = instance.Port.Port
}
targetAddress := discoveryutils.JoinHostPort(instance.HostName, instancePort)
m := promutils.NewLabels(24)
m.Add("__address__", targetAddress)
m.Add("instance", instance.InstanceID)
m.Add("__meta_eureka_app_name", app.Name)
m.Add("__meta_eureka_app_instance_hostname", instance.HostName)
m.Add("__meta_eureka_app_instance_homepage_url", instance.HomePageURL)
m.Add("__meta_eureka_app_instance_statuspage_url", instance.StatusPageURL)
m.Add("__meta_eureka_app_instance_healthcheck_url", instance.HealthCheckURL)
m.Add("__meta_eureka_app_instance_ip_addr", instance.IPAddr)
m.Add("__meta_eureka_app_instance_vip_address", instance.VipAddress)
m.Add("__meta_eureka_app_instance_secure_vip_address", instance.SecureVipAddress)
m.Add("__meta_eureka_app_instance_status", instance.Status)
m.Add("__meta_eureka_app_instance_country_id", strconv.Itoa(instance.CountryID))
m.Add("__meta_eureka_app_instance_id", instance.InstanceID)
if instance.Port.Port != 0 {
m.Add("__meta_eureka_app_instance_port", strconv.Itoa(instance.Port.Port))
m.Add("__meta_eureka_app_instance_port_enabled", strconv.FormatBool(instance.Port.Enabled))
}
if instance.SecurePort.Port != 0 {
m.Add("__meta_eureka_app_instance_secure_port", strconv.Itoa(instance.SecurePort.Port))
m.Add("__meta_eureka_app_instance_secure_port_enabled", strconv.FormatBool(instance.SecurePort.Enabled))
}
if len(instance.DataCenterInfo.Name) > 0 {
m.Add("__meta_eureka_app_instance_datacenterinfo_name", instance.DataCenterInfo.Name)
for _, tag := range instance.DataCenterInfo.Metadata.Items {
m.Add(discoveryutils.SanitizeLabelName("__meta_eureka_app_instance_datacenterinfo_metadata_"+tag.XMLName.Local), tag.Content)
}
}
for _, tag := range instance.Metadata.Items {
m.Add(discoveryutils.SanitizeLabelName("__meta_eureka_app_instance_metadata_"+tag.XMLName.Local), tag.Content)
}
ms = append(ms, m)
}
}
return ms
}