From 59cf6c03045031604a6e2bee05b8ef51f1e355d7 Mon Sep 17 00:00:00 2001 From: "te.li" Date: Wed, 18 Mar 2026 20:01:40 +0800 Subject: [PATCH] exporter: parallelize metrics generation and refresh in background Signed-off-by: te.li --- charts/hami-webui/templates/configmap.yaml | 4 +- charts/hami-webui/values.yaml | 5 +- server/config/config.yaml | 4 +- server/go.mod | 2 +- server/internal/conf/conf.proto | 2 + server/internal/exporter/exporter.go | 292 +++++++++++---------- server/internal/server/http.go | 26 +- 7 files changed, 187 insertions(+), 148 deletions(-) diff --git a/charts/hami-webui/templates/configmap.yaml b/charts/hami-webui/templates/configmap.yaml index 31dceb93..391d61c5 100644 --- a/charts/hami-webui/templates/configmap.yaml +++ b/charts/hami-webui/templates/configmap.yaml @@ -15,7 +15,9 @@ data: prometheus: address: {{ ternary .Values.externalPrometheus.address (printf "http://%s-kube-prometh-prometheus.%s.svc.cluster.local:9090" (include "hami-webui.fullname" .) (include "hami-webui.namespace" .)) .Values.externalPrometheus.enabled }} timeout: 1m + exporter_concurrency_limit: {{ .Values.exporterConcurrencyLimit }} + metrics_generate_interval: {{ .Values.metricsGenerateInterval }} node_selectors: {{- range $key, $value := .Values.vendorNodeSelectors }} {{ $key }}: {{ $value }} - {{- end }} \ No newline at end of file + {{- end }} diff --git a/charts/hami-webui/values.yaml b/charts/hami-webui/values.yaml index 8ad9e530..dc348fa2 100644 --- a/charts/hami-webui/values.yaml +++ b/charts/hami-webui/values.yaml @@ -126,6 +126,9 @@ tolerations: [] affinity: {} +exporterConcurrencyLimit: 16 +metricsGenerateInterval: 15s + dcgm-exporter: enabled: true serviceMonitor: @@ -162,4 +165,4 @@ kube-prometheus-stack: externalPrometheus: enabled: false # If externalPrometheus.enabled is true, this address will be used - address: "http://prometheus-kube-prometheus-prometheus.prometheus.svc.cluster.local:9090" \ No newline at end of file + address: "http://prometheus-kube-prometheus-prometheus.prometheus.svc.cluster.local:9090" diff --git a/server/config/config.yaml b/server/config/config.yaml index 4f481e77..eb7fe83e 100644 --- a/server/config/config.yaml +++ b/server/config/config.yaml @@ -13,4 +13,6 @@ node_selectors: Ascend: ascend=on DCU: dcu=on MLU: mlu=on - Metax: metax-tech.com/gpu.installed=true \ No newline at end of file + Metax: metax-tech.com/gpu.installed=true +exporter_concurrency_limit: 16 +metrics_generate_interval: 15s diff --git a/server/go.mod b/server/go.mod index f7bc57ea..dd145b34 100644 --- a/server/go.mod +++ b/server/go.mod @@ -12,6 +12,7 @@ require ( github.com/prometheus/client_golang v1.19.1 github.com/prometheus/common v0.55.0 go.uber.org/automaxprocs v1.5.3 + golang.org/x/sync v0.7.0 google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 google.golang.org/grpc v1.64.0 google.golang.org/protobuf v1.34.2 @@ -63,7 +64,6 @@ require ( github.com/spf13/pflag v1.0.5 // indirect golang.org/x/net v0.27.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect - golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.22.0 // indirect golang.org/x/term v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect diff --git a/server/internal/conf/conf.proto b/server/internal/conf/conf.proto index 7eb44e68..c0102803 100644 --- a/server/internal/conf/conf.proto +++ b/server/internal/conf/conf.proto @@ -9,6 +9,8 @@ message Bootstrap { Server server = 1; Prometheus prometheus = 2; map node_selectors = 3; + int32 exporter_concurrency_limit = 5; + string metrics_generate_interval = 6; } message Server { diff --git a/server/internal/exporter/exporter.go b/server/internal/exporter/exporter.go index 87583965..eca56ed1 100644 --- a/server/internal/exporter/exporter.go +++ b/server/internal/exporter/exporter.go @@ -6,15 +6,17 @@ import ( "fmt" "math" "strings" - "time" + pb "vgpu/api/v1" "vgpu/internal/biz" + "vgpu/internal/conf" "vgpu/internal/data/prom" "vgpu/internal/provider/metax" "vgpu/internal/provider/mlu" "vgpu/internal/service" "github.com/google/wire" + "golang.org/x/sync/errgroup" ) // ProviderSet is service providers. @@ -23,11 +25,11 @@ var ProviderSet = wire.NewSet( ) type MetricsGenerator struct { - promClient *prom.Client - nodeUsecase *biz.NodeUsecase - podUsecase *biz.PodUseCase - monitorService *service.MonitorService - cacheTime time.Time + promClient *prom.Client + nodeUsecase *biz.NodeUsecase + podUsecase *biz.PodUseCase + monitorService *service.MonitorService + concurrencyLimit int } // roundToTwoDecimal 将浮点数保留两位小数 @@ -41,38 +43,29 @@ func roundToOneDecimal(value float64) float64 { } func NewMetricsGenerator( + c *conf.Bootstrap, promClient *prom.Client, nodeUsecase *biz.NodeUsecase, podUsecase *biz.PodUseCase, monitorService *service.MonitorService, ) *MetricsGenerator { - return &MetricsGenerator{ - promClient: promClient, - nodeUsecase: nodeUsecase, - podUsecase: podUsecase, - monitorService: monitorService, + concurrency := int(c.ExporterConcurrencyLimit) + if concurrency <= 0 { + concurrency = 16 } -} -func (s *MetricsGenerator) generatorCache() time.Time { - now := time.Now() - return time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), 0, 0, now.Location()) -} - -func (s *MetricsGenerator) cacheIsValidate() bool { - if s.cacheTime == s.generatorCache() { - return true + return &MetricsGenerator{ + promClient: promClient, + nodeUsecase: nodeUsecase, + podUsecase: podUsecase, + monitorService: monitorService, + concurrencyLimit: concurrency, } - return false } func (s *MetricsGenerator) GenerateMetrics(ctx context.Context) error { - //if s.cacheIsValidate() { - // return nil - //} reset() // 重置所有指标缓存值 s.GenerateDeviceMetrics(ctx) // 卡维度指标 s.GenerateContainerMetrics(ctx) // 任务维度指标 - s.cacheTime = s.generatorCache() return nil } @@ -82,69 +75,76 @@ func (s *MetricsGenerator) GenerateDeviceMetrics(ctx context.Context) error { if err != nil { return err } - for _, device := range deviceInfos { - provider := device.Provider - // 查询device的驱动版本以及设备号 - deviceAdditional, err := s.queryDeviceAdditional(ctx, provider, device.Id) - var driver, deviceNo = "", "" - if err == nil && deviceAdditional != nil { - driver = deviceAdditional.DriverVersion - deviceNo = deviceAdditional.DeviceNo - } + g, ctx := errgroup.WithContext(ctx) + g.SetLimit(s.concurrencyLimit) + for _, d := range deviceInfos { + device := d + g.Go(func() error { + provider := device.Provider + // 查询device的驱动版本以及设备号 + deviceAdditional, err := s.queryDeviceAdditional(ctx, provider, device.Id) + var driver, deviceNo = "", "" + if err == nil && deviceAdditional != nil { + driver = deviceAdditional.DriverVersion + deviceNo = deviceAdditional.DeviceNo + } - // 分配率指标 - HamiVgpuCount.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(device.Count)) - HamiVmemorySize.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(device.Devmem)) - HamiVcoreSize.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(device.Devcore)) - // 超配比指标 - HamiVCoreScaling.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(device.Devcore) / 100) - HamiCoreSize.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(100) - deviceMemUsed, err := s.deviceMemUsed(ctx, provider, device.Id) - if err == nil { - HamiMemoryUsed.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(deviceMemUsed)) - } - HamiMemorySize.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(device.Devmem)) - HamiMemoryUtil.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(roundToOneDecimal(100 * float64(deviceMemUsed/float32(device.Devmem)))) - deviceMemSize, err := s.deviceMemTotal(ctx, provider, device.Id) - if err == nil && deviceMemSize > 0 { - HamiVMemoryScaling.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(roundToOneDecimal(float64(float32(device.Devmem) / deviceMemSize))) - } - actualCoreUtil, err := s.deviceCoreUtil(ctx, provider, device.Id) - if err == nil { - HamiCoreUsed.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(actualCoreUtil)) - HamiCoreUtil.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(actualCoreUtil)) - } - actualCoreUtilAvg, err := s.deviceCoreUtil(ctx, provider, device.Id) - if err == nil { - HamiCoreUsedAvg.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(actualCoreUtilAvg)) - HamiCoreUtilAvg.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(actualCoreUtilAvg)) - } - gpuTemperature, err := s.gpuTemperature(ctx, provider, device.Id) - if err == nil { - HamiDeviceTemperature.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(gpuTemperature)) - } - memoryTemperature, err := s.memoryTemperature(ctx, provider, device.Id) - if err == nil { - HamiDeviceMemoryTemperature.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(memoryTemperature)) - } - gpuPower, err := s.gpuPower(ctx, provider, device.Id) - if err == nil { - HamiDevicePower.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(gpuPower)) - } - fanSpeed, err := s.fanSpeed(ctx, provider, device.Id) - if err == nil { - switch provider { - case biz.NvidiaGPUDevice: - HamiDeviceFanSpeedP.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(fanSpeed)) - case biz.CambriconGPUDevice: - HamiDeviceFanSpeedR.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(fanSpeed)) + // 分配率指标 + HamiVgpuCount.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(device.Count)) + HamiVmemorySize.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(device.Devmem)) + HamiVcoreSize.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(device.Devcore)) + // 超配比指标 + HamiVCoreScaling.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(device.Devcore) / 100) + HamiCoreSize.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(100) + deviceMemUsed, err := s.deviceMemUsed(ctx, provider, device.Id) + if err == nil { + HamiMemoryUsed.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(deviceMemUsed)) } - } - gpuHardwareHealth, err := s.gpuHardwareHealth(ctx, provider, device.Id) - if err == nil { - HamiDeviceHardwareHealth.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(gpuHardwareHealth)) - } + HamiMemorySize.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(device.Devmem)) + HamiMemoryUtil.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(roundToOneDecimal(100 * float64(deviceMemUsed/float32(device.Devmem)))) + deviceMemSize, err := s.deviceMemTotal(ctx, provider, device.Id) + if err == nil && deviceMemSize > 0 { + HamiVMemoryScaling.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(roundToOneDecimal(float64(float32(device.Devmem) / deviceMemSize))) + } + actualCoreUtil, err := s.deviceCoreUtil(ctx, provider, device.Id) + if err == nil { + HamiCoreUsed.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(actualCoreUtil)) + HamiCoreUtil.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(actualCoreUtil)) + } + actualCoreUtilAvg, err := s.deviceCoreUtil(ctx, provider, device.Id) + if err == nil { + HamiCoreUsedAvg.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(actualCoreUtilAvg)) + HamiCoreUtilAvg.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(actualCoreUtilAvg)) + } + gpuTemperature, err := s.gpuTemperature(ctx, provider, device.Id) + if err == nil { + HamiDeviceTemperature.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(gpuTemperature)) + } + memoryTemperature, err := s.memoryTemperature(ctx, provider, device.Id) + if err == nil { + HamiDeviceMemoryTemperature.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(memoryTemperature)) + } + gpuPower, err := s.gpuPower(ctx, provider, device.Id) + if err == nil { + HamiDevicePower.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(gpuPower)) + } + fanSpeed, err := s.fanSpeed(ctx, provider, device.Id) + if err == nil { + switch provider { + case biz.NvidiaGPUDevice: + HamiDeviceFanSpeedP.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(fanSpeed)) + case biz.CambriconGPUDevice: + HamiDeviceFanSpeedR.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(fanSpeed)) + } + } + gpuHardwareHealth, err := s.gpuHardwareHealth(ctx, provider, device.Id) + if err == nil { + HamiDeviceHardwareHealth.WithLabelValues(device.NodeName, provider, device.Type, device.Id, driver, deviceNo).Set(float64(gpuHardwareHealth)) + } + return nil + }) } + _ = g.Wait() return nil } @@ -216,69 +216,77 @@ func (s *MetricsGenerator) GenerateContainerMetrics(ctx context.Context) error { } s.generateMetricsForMetaxGPU(containers) - for _, device := range deviceInfos { - for _, c := range containers { - var vGPU int32 = 0 - var core int32 = 0 - var memory int32 = 0 - var provider string = "" - for _, cd := range c.ContainerDevices { - if device.AliasId != "" && !strings.HasPrefix(cd.UUID, device.AliasId) { - continue + g, ctx := errgroup.WithContext(ctx) + g.SetLimit(s.concurrencyLimit) + for _, d := range deviceInfos { + device := d + for _, cont := range containers { + c := cont + g.Go(func() error { + var vGPU int32 = 0 + var core int32 = 0 + var memory int32 = 0 + var provider string = "" + for _, cd := range c.ContainerDevices { + if device.AliasId != "" && !strings.HasPrefix(cd.UUID, device.AliasId) { + continue + } + vGPU = vGPU + 1 + core = core + cd.Usedcores + memory = memory + cd.Usedmem + provider = cd.Type } - vGPU = vGPU + 1 - core = core + cd.Usedcores - memory = memory + cd.Usedmem - provider = cd.Type - } - if provider == "" || provider == metax.MetaxGPUDevice { - continue - } - HamiContainerVgpuAllocated.WithLabelValues(device.NodeName, provider, device.Type, device.Id, c.PodName, c.Name, c.Namespace, fmt.Sprintf("%s:%s", c.Name, c.PodUID)).Set(float64(vGPU)) - HamiContainerVmemoryAllocated.WithLabelValues(device.NodeName, provider, device.Type, device.Id, c.PodName, c.Name, c.Namespace, fmt.Sprintf("%s:%s", c.Name, c.PodUID)).Set(float64(memory)) - HamiContainerVcoreAllocated.WithLabelValues(device.NodeName, provider, device.Type, device.Id, c.PodName, c.Name, c.Namespace, fmt.Sprintf("%s:%s", c.Name, c.PodUID)).Set(float64(core)) - // 查询任务在当前设备下的算力利用率 - taskCoreUsed, err := s.taskCoreUsed(ctx, provider, c.Namespace, c.PodName, c.Name, c.PodUID, device.Id, device.NodeName, device.Index) - if err == nil { - used := float64(0) - util := float64(0) - switch provider { - case biz.NvidiaGPUDevice: - used = float64(taskCoreUsed) - util = roundToOneDecimal(100 * float64(taskCoreUsed) / float64(core)) - case biz.CambriconGPUDevice: - used = float64(taskCoreUsed) / 100 * float64(core) - util = float64(taskCoreUsed) - case biz.HygonGPUDevice: - used = float64(taskCoreUsed) - util = roundToOneDecimal(100 * float64(taskCoreUsed) / float64(core)) - case metax.MetaxSGPUDevice: - used = float64(taskCoreUsed) - util = roundToOneDecimal(100 * float64(taskCoreUsed) / float64(core)) - default: + if provider == "" || provider == metax.MetaxGPUDevice { + return nil } - cardCoreUtil, err := s.deviceCoreUtil(ctx, provider, device.Id) - if err == nil && used != 0 && cardCoreUtil > 95 { - used = float64(cardCoreUtil) / 100 * float64(core) - util = float64(cardCoreUtil) + HamiContainerVgpuAllocated.WithLabelValues(device.NodeName, provider, device.Type, device.Id, c.PodName, c.Name, c.Namespace, fmt.Sprintf("%s:%s", c.Name, c.PodUID)).Set(float64(vGPU)) + HamiContainerVmemoryAllocated.WithLabelValues(device.NodeName, provider, device.Type, device.Id, c.PodName, c.Name, c.Namespace, fmt.Sprintf("%s:%s", c.Name, c.PodUID)).Set(float64(memory)) + HamiContainerVcoreAllocated.WithLabelValues(device.NodeName, provider, device.Type, device.Id, c.PodName, c.Name, c.Namespace, fmt.Sprintf("%s:%s", c.Name, c.PodUID)).Set(float64(core)) + // 查询任务在当前设备下的算力利用率 + taskCoreUsed, err := s.taskCoreUsed(ctx, provider, c.Namespace, c.PodName, c.Name, c.PodUID, device.Id, device.NodeName, device.Index) + if err == nil { + used := float64(0) + util := float64(0) + switch provider { + case biz.NvidiaGPUDevice: + used = float64(taskCoreUsed) + util = roundToOneDecimal(100 * float64(taskCoreUsed) / float64(core)) + case biz.CambriconGPUDevice: + used = float64(taskCoreUsed) / 100 * float64(core) + util = float64(taskCoreUsed) + case biz.HygonGPUDevice: + used = float64(taskCoreUsed) + util = roundToOneDecimal(100 * float64(taskCoreUsed) / float64(core)) + case metax.MetaxSGPUDevice: + used = float64(taskCoreUsed) + util = roundToOneDecimal(100 * float64(taskCoreUsed) / float64(core)) + default: + } + cardCoreUtil, err := s.deviceCoreUtil(ctx, provider, device.Id) + if err == nil && used != 0 && cardCoreUtil > 95 { + used = float64(cardCoreUtil) / 100 * float64(core) + util = float64(cardCoreUtil) + } + HamiContainerCoreUsed.WithLabelValues(device.NodeName, provider, device.Type, device.Id, c.PodName, c.Name, c.Namespace).Set(used) + HamiContainerCoreUtil.WithLabelValues(device.NodeName, provider, device.Type, device.Id, c.PodName, c.Name, c.Namespace).Set(util) } - HamiContainerCoreUsed.WithLabelValues(device.NodeName, provider, device.Type, device.Id, c.PodName, c.Name, c.Namespace).Set(used) - HamiContainerCoreUtil.WithLabelValues(device.NodeName, provider, device.Type, device.Id, c.PodName, c.Name, c.Namespace).Set(util) - } - taskMemoryUsed, err := s.taskMemoryUsed(ctx, provider, c.Namespace, c.PodName, c.Name, c.PodUID, device.Id, device.NodeName, device.Index) - if err == nil { - switch provider { - case biz.CambriconGPUDevice: - taskMemoryUsed = float32((taskMemoryUsed/100)*float32(memory)) * 1024 * 1024 - case metax.MetaxSGPUDevice: - taskMemoryUsed = float32(taskMemoryUsed) * 1024 // KB->Byte - default: + taskMemoryUsed, err := s.taskMemoryUsed(ctx, provider, c.Namespace, c.PodName, c.Name, c.PodUID, device.Id, device.NodeName, device.Index) + if err == nil { + switch provider { + case biz.CambriconGPUDevice: + taskMemoryUsed = float32((taskMemoryUsed/100)*float32(memory)) * 1024 * 1024 + case metax.MetaxSGPUDevice: + taskMemoryUsed = float32(taskMemoryUsed) * 1024 // KB->Byte + default: + } + HamiContainerMemoryUsed.WithLabelValues(device.NodeName, provider, device.Type, device.Id, c.PodName, c.Name, c.Namespace).Set(float64(taskMemoryUsed / 1024 / 1024)) + HamiContainerMemoryUtil.WithLabelValues(device.NodeName, provider, device.Type, device.Id, c.PodName, c.Name, c.Namespace).Set(roundToOneDecimal(100 * float64(taskMemoryUsed/1024/1024) / float64(memory))) } - HamiContainerMemoryUsed.WithLabelValues(device.NodeName, provider, device.Type, device.Id, c.PodName, c.Name, c.Namespace).Set(float64(taskMemoryUsed / 1024 / 1024)) - HamiContainerMemoryUtil.WithLabelValues(device.NodeName, provider, device.Type, device.Id, c.PodName, c.Name, c.Namespace).Set(roundToOneDecimal(100 * float64(taskMemoryUsed/1024/1024) / float64(memory))) - } + return nil + }) } } + _ = g.Wait() return nil } diff --git a/server/internal/server/http.go b/server/internal/server/http.go index 2625c146..6f7be33a 100644 --- a/server/internal/server/http.go +++ b/server/internal/server/http.go @@ -1,6 +1,9 @@ package server import ( + "context" + "time" + v1 "vgpu/api/v1" "vgpu/internal/conf" "vgpu/internal/exporter" @@ -43,9 +46,28 @@ func NewHTTPServer(c *conf.Bootstrap, v1.RegisterContainerHTTPServer(srv, ctr) v1.RegisterMonitorHTTPServer(srv, monitor) srv.HandlePrefix("/q/", openapiv2.NewHandler()) + intervalStr := c.MetricsGenerateInterval + if intervalStr == "" { + intervalStr = "15s" + } + interval, err := time.ParseDuration(intervalStr) + if err != nil || interval <= 0 { + interval = 15 * time.Second + } + go func() { + ctx, cancel := context.WithTimeout(context.Background(), interval) + _ = exporter.GenerateMetrics(ctx) + cancel() + + ticker := time.NewTicker(interval) + defer ticker.Stop() + for range ticker.C { + ctx, cancel := context.WithTimeout(context.Background(), interval) + _ = exporter.GenerateMetrics(ctx) + cancel() + } + }() srv.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) { - exporter.GenerateMetrics(r.Context()) - //mock.MockMetrics(r.Context()) promhttp.Handler().ServeHTTP(w, r) }) return srv