当前位置: 首页 > news >正文

GO 集成Prometheus

一、Prometheus介绍

Prometheus(普罗米修斯)是一套开源的监控&报警&时间序列数据库的组合,起始是由SoundCloud公司开发的。随着发展,越来越多公司和组织接受采用Prometheus,社会也十分活跃,他们便将它独立成开源项目,并且有公司来运作。Google SRE的书内也曾提到跟他们BorgMon监控系统相似的实现是Prometheus。现在最常见的Kubernetes容器管理系统中,通常会搭配Prometheus进行监控。

Prometheus基本原理是通过HTTP协议周期性抓取被监控组件的状态,这样做的好处是任意组件只要提供HTTP接口就可以接入监控系统,不需要任何SDK或者其他的集成过程。这样做非常适合虚拟化环境比如VM或者Docker 。

Prometheus应该是为数不多的适合Docker、Mesos、Kubernetes环境的监控系统之一。

输出被监控组件信息的HTTP接口被叫做exporter 。目前互联网公司常用的组件大部分都有exporter可以直接使用,比如Varnish、Haproxy、Nginx、MySQL、Linux 系统信息 (包括磁盘、内存、CPU、网络等等),具体支持的源看:https://github.com/prometheus。

与其他监控系统相比,Prometheus的主要特点是:

  • 一个多维数据模型(时间序列由指标名称定义和设置键/值尺寸)。
  • 非常高效的存储,平均一个采样数据占~3.5bytes左右,320万的时间序列,每30秒采样,保持60天,消耗磁盘大概228G。
  • 一种灵活的查询语言。
  • 不依赖分布式存储,单个服务器节点。
  • 时间集合通过HTTP上的PULL模型进行。
  • 通过中间网关支持推送时间。
  • 通过服务发现或静态配置发现目标。
  • 多种模式的图形和仪表板支持。

二、Prometheus的架构

img

Prometheus Server 直接从监控目标中或者间接通过推送网关来拉取监控指标,它在本地存储所有抓取到的样本数据,并对此数据执行一系列规则,以汇总和记录现有数据的新时间序列或生成告警。可以通过 Grafana 或者其他工具来实现监控数据的可视化。

Prometheus 适用于什么场景

Prometheus 适用于记录文本格式的时间序列,它既适用于以机器为中心的监控,也适用于高度动态的面向服务架构的监控。在微服务的世界中,它对多维数据收集和查询的支持有特殊优势。Prometheus 是专为提高系统可靠性而设计的,它可以在断电期间快速诊断问题,每个 Prometheus Server 都是相互独立的,不依赖于网络存储或其他远程服务。当基础架构出现故障时,你可以通过 Prometheus 快速定位故障点,而且不会消耗大量的基础架构资源。

Prometheus 不适合什么场景

Prometheus 非常重视可靠性,即使在出现故障的情况下,你也可以随时查看有关系统的可用统计信息。如果你需要百分之百的准确度,例如按请求数量计费,那么 Prometheus 不太适合你,因为它收集的数据可能不够详细完整。这种情况下,你最好使用其他系统来收集和分析数据以进行计费,并使用 Prometheus 来监控系统的其余部分。

三、数据模型

Prometheus 所有采集的监控数据均以指标(metric)的形式保存在内置的时间序列数据库当中(TSDB):属于同一指标名称,同一标签集合的、有时间戳标记的数据流。除了存储的时间序列,Prometheus 还可以根据查询请求产生临时的、衍生的时间序列作为返回结果。

指标名称和标签

每一条时间序列由指标名称(Metrics Name)以及一组标签(键值对)唯一标识。其中指标的名称(metric name)可以反映被监控样本的含义(例如,http_requests_total — 表示当前系统接收到的 HTTP 请求总量),指标名称只能由 ASCII 字符、数字、下划线以及冒号组成,同时必须匹配正则表达式 [a-zA-Z_:][a-zA-Z0-9_:]*

[info] 注意

冒号用来表示用户自定义的记录规则,不能在 exporter 中或监控对象直接暴露的指标中使用冒号来定义指标名称。

通过使用标签,Prometheus 开启了强大的多维数据模型:对于相同的指标名称,通过不同标签列表的集合,会形成特定的度量维度实例(例如:所有包含度量名称为 /api/tracks 的 http 请求,打上 method=POST 的标签,就会形成具体的 http 请求)。该查询语言在这些指标和标签列表的基础上进行过滤和聚合。改变任何度量指标上的任何标签值(包括添加或删除指标),都会创建新的时间序列。

标签的名称只能由 ASCII 字符、数字以及下划线组成并满足正则表达式 [a-zA-Z_][a-zA-Z0-9_]*。其中以 __ 作为前缀的标签,是系统保留的关键字,只能在系统内部使用。标签的值则可以包含任何 Unicode 编码的字符。

更多详细内容请参考 指标和标签命名最佳实践。

样本

在时间序列中的每一个点称为一个样本(sample),样本由以下三部分组成:

指标(metric):指标名称和描述当前样本特征的 labelsets;

时间戳(timestamp):一个精确到毫秒的时间戳;

样本值(value): 一个 folat64 的浮点型数据表示当前样本的值。

表示方式

通过如下表达方式表示指定指标名称和指定标签集合的时间序列:

{

例如,指标名称为 api_http_requests_total,标签为 method="POST"handler="/messages" 的时间序列可以表示为:

api_http_requests_total{method=“POST”, handler=“/messages”}

四、四种数据类型

4.1Counter

Counter用于累计值,例如记录请求次数、任务完成数、错误发生次数。一直增加,不会减少。重启进程后,会被重置。

例如:http_response_total{method=”GET”,endpoint=”/api/tracks”} 100,10秒后抓取http_response_total{method=”GET”,endpoint=”/api/tracks”} 100。

4.2Gauge

Gauge常规数值,例如 温度变化、内存使用变化。可变大,可变小。重启进程后,会被重置。

例如: memory_usage_bytes{host=”master-01″} 100 < 抓取值、memory_usage_bytes{host=”master-01″} 30、memory_usage_bytes{host=”master-01″} 50、memory_usage_bytes{host=”master-01″} 80 < 抓取值。

4.3Histogram

Histogram(直方图)可以理解为柱状图的意思,常用于跟踪事件发生的规模,例如:请求耗时、响应大小。它特别之处是可以对记录的内容进行分组,提供count和sum全部值的功能。

例如:{小于10=5次,小于20=1次,小于30=2次},count=7次,sum=7次的求和值。

4.4Summary

Summary和Histogram十分相似,常用于跟踪事件发生的规模,例如:请求耗时、响应大小。同样提供 count 和 sum 全部值的功能。

例如:count=7次,sum=7次的值求值。

它提供一个quantiles的功能,可以按%比划分跟踪的结果。例如:quantile取值0.95,表示取采样值里面的95%数据。

五、安装

5.1下载镜像包
docker pull prom/node-exporter
docker pull prom/prometheus
docker pull grafana/grafana
5.2启动node-exporter
docker run -d -p 9100:9100 -v "/proc:/host/proc:ro" -v "/sys:/host/sys:ro" -v "/:/rootfs:ro" --net="host" prom/node-exporter

等几秒中后查看端口

在这里插入图片描述

访问如下地址:http://192.168.66.130:9100/metrics,则可看到相应的结果

5.3启动prometheus

先建立配置文件的目录

mkdir /opt/prometheus
cd /opt/prometheus
vim prometheus.yaml

prometheus.yaml的文件内容如下

global:scrape_interval:     60sevaluation_interval: 60sscrape_configs:- job_name: prometheusstatic_configs:- targets: ['localhost:9090']labels:instance: prometheus- job_name: linuxstatic_configs:- targets: ['192.168.91.132:9100']labels:instance: localhost

启动

docker run -d -p 9090:9090 -v /opt/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus

过几分钟后查看端口

在这里插入图片描述

5.4启动grafana
mkdir /opt/grafana-storage
chmod 777 -R /opt/grafana-storage
docker run -d -p 3000:3000 --name=grafana -v /opt/grafana-storage:/var/lib/grafana grafana/grafana

启动http://192.168.66.130:3000,

首次访问要输入用户名和密码,默认 是admin:admin

六、grafana基本操作

6.1创建数据源

点击在这里插入图片描述

进去后,选择prometheus,然后配置

在这里插入图片描述

6.2导入模板

在这里插入图片描述
在这里插入图片描述

七、 golang集成promethes

7.1Gin集成promethes
package mainimport ("github.com/gin-gonic/gin""github.com/prometheus/client_golang/prometheus""github.com/prometheus/client_golang/prometheus/promauto""github.com/prometheus/client_golang/prometheus/promhttp""time"
)func recordMetrics() {for {ops.Inc()time.Sleep(2 * time.Second)}
}var (ops = promauto.NewCounter(prometheus.CounterOpts{Name: "mxshop_test",Help: "just for test",})
)func main() {go recordMetrics()r := gin.Default()r.GET("/metrics", gin.WrapH(promhttp.Handler()))r.Run(":8050")
}
7.2、rpcserver的interceptor集成prometheus

在rpcserver端创建prometheus建建interceptor,具体的代码如下

package serverinterceptorsimport ("context""strconv""time""google.golang.org/grpc""google.golang.org/grpc/status""mxshop/gmicro/core/metric"
)/**
两个基本指示,1.每个请求的耗时(hisogram)2.每个请求的状态计数器(counter)
/user 状态码 有label 主要是状态码
*/const serverNamespace = "rpc_server"/*
两个基本指标。 1. 每个请求的耗时(histogram) 2. 每个请求的状态计数器(counter)
/user 状态码 有label 主要是状态码
*/var (metricServerReqDur = metric.NewHistogramVec(&metric.HistogramVecOpts{Namespace: serverNamespace,Subsystem: "requests",Name:      "chaos_duration_ms",Help:      "rpc server requests duration(ms).",Labels:    []string{"method"},Buckets:   []float64{5, 10, 25, 50, 100, 250, 500, 1000},})metricServerReqCodeTotal = metric.NewCounterVec(&metric.CounterVecOpts{Namespace: serverNamespace,Subsystem: "requests",Name:      "chaos_code_total",Help:      "rpc server requests code count.",Labels:    []string{"method", "code"},})
)func UnaryPrometheusInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo,handler grpc.UnaryHandler) (resp interface{}, err error) {startTime := time.Now()resp, err = handler(ctx, req)//记录了耗时metricServerReqDur.Observe(int64(time.Since(startTime)/time.Millisecond), info.FullMethod)//记录了状态码metricServerReqCodeTotal.Inc(info.FullMethod, strconv.Itoa(int(status.Code(err))))return resp, err
}

rpcserver 方法中的NewServer中添加

package rpcserverimport ("context""go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc""google.golang.org/grpc""google.golang.org/grpc/health""google.golang.org/grpc/health/grpc_health_v1""google.golang.org/grpc/reflection"apimd "mxshop/api/metadata"srvintc "mxshop/gmicro/server/rpcserver/serverinterceptors""mxshop/pkg/host""mxshop/pkg/log""net""net/url""time"
)type ServerOption func(o *Server)type Server struct {*grpc.Serveraddress   stringunaryInts []grpc.UnaryServerInterceptorstreamIns []grpc.StreamServerInterceptorgrpcOpts  []grpc.ServerOptionlis       net.Listenertimeout   time.Durationhealth   *health.Servermetadata *apimd.Serverendpoint *url.URL//是否开启 metric 监测enableMetric bool
}func NewServer(opts ...ServerOption) *Server {srv := &Server{address: ":0",health:  health.NewServer(),//timeout: 1 * time.Second,}for _, o := range opts {o(srv)}//TODO 我们现在希望用户不设置拦截器的情况下,我们会自动默认加上一些必须的拦截器 crashunaryInts := []grpc.UnaryServerInterceptor{srvintc.UnaryCrashInterceptor,otelgrpc.UnaryServerInterceptor(),}//就是这一步了UnaryPrometheusInterceptorif srv.enableMetric {unaryInts = append(unaryInts, srvintc.UnaryPrometheusInterceptor)}if srv.timeout > 0 {unaryInts = append(unaryInts, srvintc.UnaryTimeoutInterceptor(srv.timeout))}if len(srv.unaryInts) > 0 {unaryInts = append(unaryInts, srv.unaryInts...)}//把我们传入的拦截器转换成grpc的ServerOptiongrpcOpts := []grpc.ServerOption{grpc.ChainUnaryInterceptor(srv.unaryInts...)}//把用户自已传入的grpc.ServerOption放在一起if len(srv.grpcOpts) > 0 {grpcOpts = append(grpcOpts, srv.grpcOpts...)}srv.Server = grpc.NewServer(grpcOpts...)//注册metadata的serversrv.metadata = apimd.NewServer(srv.Server)//解析addressif err := srv.listenAndEndpotion(); err != nil {return nil}//注册healthgrpc_health_v1.RegisterHealthServer(srv.Server, srv.health)apimd.RegisterMetadataServer(srv.Server, srv.metadata)reflection.Register(srv.Server)//可以支持用户直接通过grpc的一个接口查看当前支持的所有的rpc服务return srv
}func (s *Server) Address() string {return s.address
}func WithAddress(address string) ServerOption {return func(s *Server) {s.address = address}
}func WithTimeout(timeout time.Duration) ServerOption {return func(s *Server) {s.timeout = timeout}
}func WithLis(lis net.Listener) ServerOption {return func(s *Server) {s.lis = lis}
}func WithUnaryInterceptor(in ...grpc.UnaryServerInterceptor) ServerOption {return func(s *Server) {s.unaryInts = in}
}func WithStreamInterceptor(in ...grpc.StreamServerInterceptor) ServerOption {return func(s *Server) {s.streamIns = in}
}func WithOptions(opts ...grpc.ServerOption) ServerOption {return func(s *Server) {s.grpcOpts = opts}
}// 完成ip和端口的提取
func (s *Server) listenAndEndpotion() error {if s.lis == nil {lis, err := net.Listen("tcp", s.address)if err != nil {return err}s.lis = lis}addr, err := host.Extract(s.address, s.lis)if err != nil {_ = s.lis.Close()return err}s.endpoint = &url.URL{Scheme: "grpc", Host: addr}return nil
}
func WithEnableMetric(enable bool) ServerOption {return func(s *Server) { s.enableMetric = enable }
}func (s *Server) Start(ctx context.Context) error {log.Infof("[grpc] server listening on: %s", s.lis.Addr().String())//改grpc核心变量 状态//只有.Resume()之后,请求才能进来//s.health.Shutdown()相反s.health.Resume()return s.Server.Serve(s.lis)}
func (s *Server) Stop(ctx context.Context) error {//设置服务的状态为not_serving 防止接受新的请求s.health.Shutdown()s.Server.GracefulStop()log.Infof("[grpc] server stopped")return nil
}
7.3. rpcclient的 interceptor集成prometheus
package clientinterceptorsimport ("context""google.golang.org/grpc""google.golang.org/grpc/status""mxshop/gmicro/core/metric""strconv""time"
)const serverNamespace = "rpc_client"/*
两个基本指标。 1. 每个请求的耗时(histogram) 2. 每个请求的状态计数器(counter)
/user 状态码 有label 主要是状态码
*/var (metricServerReqDur = metric.NewHistogramVec(&metric.HistogramVecOpts{Namespace: serverNamespace,Subsystem: "requests",Name:      "chaos_duration_ms",Help:      "rpc server requests duration(ms).",Labels:    []string{"method"},Buckets:   []float64{5, 10, 25, 50, 100, 250, 500, 1000},})metricServerReqCodeTotal = metric.NewCounterVec(&metric.CounterVecOpts{Namespace: serverNamespace,Subsystem: "requests",Name:      "chaos_code_total",Help:      "rpc server requests code count.",Labels:    []string{"method", "code"},})
)func PrometheusInterceptor() grpc.UnaryClientInterceptor {return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn,invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {startTime := time.Now()err := invoker(ctx, method, req, reply, cc, opts...)//记录了耗时metricServerReqDur.Observe(int64(time.Since(startTime)/time.Millisecond), method)//记录了状态码metricServerReqCodeTotal.Inc(method, strconv.Itoa(int(status.Code(err))))return err}
}

在rpcclient中的dial方法中添加这个interceptor

package rpcserverimport ("context""go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc""google.golang.org/grpc"grpcinsecure "google.golang.org/grpc/credentials/insecure""mxshop/gmicro/registry""mxshop/gmicro/server/rpcserver/clientinterceptors""mxshop/gmicro/server/rpcserver/resolver/discovery""mxshop/pkg/log""time"
)type ClientOption func(o *clientOptions)
type clientOptions struct {// 服务端的地址endpoint string// 超时时间timeout time.Duration// 服务发现接口discovery registry.Discovery// Unary 服务的拦截器unaryInts []grpc.UnaryClientInterceptor// Stream 服务的拦截器streamInts []grpc.StreamClientInterceptor// 用户自己设置 grpc 连接的结构体,例如: grpc.WithInsecure(), grpc.WithTransportCredentials()rpcOpts []grpc.DialOption// 根据 Name 生成负载均衡的策略balancerName string// 客户端的日志logger log.Logger// 是否开启链路追踪enableTracing bool//是否开启 metric 监测enableMetric bool
}// WithEndpoint 设置服务端的地址
func WithEndpoint(endpoint string) ClientOption {return func(o *clientOptions) {o.endpoint = endpoint}
}// WithClientTimeout 设置超时时间
func WithClientTimeout(timeout time.Duration) ClientOption {return func(o *clientOptions) {o.timeout = timeout}
}// WithDiscovery 设置服务发现
func WithDiscovery(d registry.Discovery) ClientOption {return func(o *clientOptions) {o.discovery = d}
}// WithClientUnaryInterceptor 设置拦截器
func WithClientUnaryInterceptor(in ...grpc.UnaryClientInterceptor) ClientOption {return func(o *clientOptions) {o.unaryInts = in}
}// WithClientStreamInterceptor 设置stream拦截器
func WithClientStreamInterceptor(in ...grpc.StreamClientInterceptor) ClientOption {return func(o *clientOptions) {o.streamInts = in}
}// WithClientOptions 设置grpc的dial选项
func WithClientOptions(opts ...grpc.DialOption) ClientOption {return func(o *clientOptions) {o.rpcOpts = opts}
}// WithBalancerName 设置负载均衡器
func WithBalancerName(name string) ClientOption {return func(o *clientOptions) {o.balancerName = name}
}// WithClientLogger 设置日志
func WithClientLogger(logger log.Logger) ClientOption {return func(o *clientOptions) {o.logger = logger}
}// WithClientTracing 设置链路追踪
func WithClientTracing() ClientOption {return func(o *clientOptions) {o.enableTracing = true}
}func WithClientEnableMetric(enable bool) ServerOption {return func(s *Server) { s.enableMetric = enable }
}// DialInsecure 非安全拨号
func DialInsecure(ctx context.Context, opts ...ClientOption) (*grpc.ClientConn, error) {return dial(ctx, true, opts...)
}func Dial(ctx context.Context, opts ...ClientOption) (*grpc.ClientConn, error) {return dial(ctx, false, opts...)
}
func dial(ctx context.Context, insecure bool, opts ...ClientOption) (*grpc.ClientConn, error) {//默认配置options := clientOptions{timeout:       200 * time.Millisecond,balancerName:  "round_robin",enableTracing: true,}for _, o := range opts {o(&options)}//TODO 客户端默认拦截器ints := []grpc.UnaryClientInterceptor{//应该是闭包特性,直接调用后返回resp供grpc拦截器调用clientinterceptors.TimeoutInterceptor(options.timeout),}//这个就是集成prometheusif options.enableMetric {ints = append(ints, clientinterceptors.PrometheusInterceptor())}if options.enableTracing {ints = append(ints, otelgrpc.UnaryClientInterceptor())}streamInts := []grpc.StreamClientInterceptor{}if len(options.unaryInts) > 0 {ints = append(ints, options.unaryInts...)}if len(options.streamInts) > 0 {streamInts = append(streamInts, options.streamInts...)}//可以由用户端自己传递 这些默认的grpcOpts := []grpc.DialOption{grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "` + options.balancerName + `"}`),grpc.WithChainUnaryInterceptor(ints...),grpc.WithChainStreamInterceptor(streamInts...),}//TODO 服务发现的选项 这里调用 resolver 的直连模式或者是服务发现模式if options.discovery != nil {grpcOpts = append(grpcOpts, grpc.WithResolvers(discovery.NewBuilder(options.discovery,discovery.WithInsecure(insecure)),))}if insecure {grpcOpts = append(grpcOpts, grpc.WithTransportCredentials(grpcinsecure.NewCredentials()))}if len(options.rpcOpts) > 0 {grpcOpts = append(grpcOpts, options.rpcOpts...)}return grpc.DialContext(ctx, options.endpoint, grpcOpts...)
}
7.4restserver端集成interceptor
package restserverimport ("context""errors""fmt""github.com/gin-gonic/gin"ut "github.com/go-playground/universal-translator""github.com/penglongli/gin-metrics/ginmetrics"mws "mxshop/gmicro/server/restserver/middlewares""mxshop/gmicro/server/restserver/pprof""mxshop/gmicro/server/restserver/validation""mxshop/pkg/log""net/http""time"
)type JwtInfo struct {//defaults to "JWT"Realm string//defaults to emptyKey string//defaults to 7 daysTimeout time.Duration//defaults to 7 days 刷新时长MaxRefresh time.Duration
}// Server wrapper for gin.Engine
type Server struct {*gin.Engine//端口号port int//开发模式mode string//是否开启健康检查接口,默认开启,如果开启会自动添加/health接口healthz bool//是否开启pprof接口,默认开启,如果开启会自动添加/debug/pprof接口enableProfiling bool//是否开启metrics接口,默认开启,如果开启会自动添加/metrics接口enableMetrics boolmiddlewares       []stringcustomMiddlewares []gin.HandlerFunc//jwt配置信息jwt *JwtInfo//翻译器 默认:zhtransName stringtrans     ut.Translatorserver *http.ServerserviceName string
}func NewServer(opts ...ServerOption) *Server {srv := &Server{port:            8080,mode:            "debug",healthz:         true,enableProfiling: true,jwt: &JwtInfo{"JWT","Gd%YCfP1agNHo5x6xm2Qs33Bf!B#Gi!o",1 * 24 * time.Hour,7 * 24 * time.Hour,},Engine:      gin.Default(),transName:   "zh",serviceName: "gmicro",}for _, o := range opts {o(srv)}srv.Use(mws.TracingHandler(srv.serviceName))for _, m := range srv.middlewares {mw, ok := mws.Middlewares[m]if !ok {log.Warnf("can not find middleware:%s", m)continue}log.Infof("install middleware:%s", m)srv.Use(mw)}return srv
}// Start  rest server
func (s *Server) Start(ctx context.Context) error {/*debug模式和release模式区别主要是打印的日志不同环境变量的模式,在docker k8s部署中很常用gin.SetMode(gin.ReleaseMode)*/if s.mode != gin.DebugMode && s.mode != gin.ReleaseMode && s.mode != gin.TestMode {return errors.New("mode must be one of debug/release/test")}gin.SetMode(s.mode)gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) {log.Infof("%-6s %-s --> %s(%d handlers)", httpMethod, absolutePath, handlerName, nuHandlers)}//TODO 初始化翻译器err := s.initTrans(s.transName)if err != nil {log.Errorf("initTrans error: %s", err.Error())return err}//注册mobile验证器validation.RegisterMobile(s.trans)//根据配置初始化pprof路由if s.enableProfiling {pprof.Register(s.Engine)}//这个就是集成prometheusif s.enableMetrics {// get global Monitor objectm := ginmetrics.GetMonitor()// +optional set metric path, default /debug/metricsm.SetMetricPath("/metrics")// +optional set slow time, default 5sm.SetSlowTime(10)// +optional set request duration, default {0.1, 0.3, 1.2, 5, 10}// used to p95, p99m.SetDuration([]float64{0.1, 0.3, 1.2, 5, 10})//反向注入m.Use(s)}log.Infof("rest server is running on port: %d", s.port)_ = s.SetTrustedProxies(nil)address := fmt.Sprintf(":%d", s.port)s.server = &http.Server{Addr:    address,Handler: s.Engine,}if err = s.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {return err}return nil
}func (s *Server) Stop(ctx context.Context) error {log.Infof("rest server is stopping on port: %d", s.port)if err := s.server.Shutdown(ctx); err != nil {log.Errorf("rest server is shutting down: %v", err)return err}log.Infof("rest server stopped on port: %d", s.port)return nil
}

在启动restserver的服务的方法增加启动prometheus

package adminimport ("mxshop/app/user/srv/config""mxshop/gmicro/server/restserver"
)func NewUserHTTPServer(cfg *config.Config) (*restserver.Server, error) {urestServer := restserver.NewServer(restserver.WithPort(cfg.Server.HttpPort),restserver.WithMiddlewares(cfg.Server.Middlewares),restserver.WithMetrics(true),)//配置好路由initRouter(urestServer)return urestServer, nil
}

7.5启动测试

先启动服务端,再启动客户端,然后通过POSTMAN方法访问

服务端启动的情况如下

在这里插入图片描述

客户端启动的情况如下:

在这里插入图片描述

在这里插入图片描述

然后在grafanaa中就可以看到结果了

在这里插入图片描述

相关文章:

GO 集成Prometheus

一、Prometheus介绍 Prometheus&#xff08;普罗米修斯&#xff09;是一套开源的监控&报警&时间序列数据库的组合&#xff0c;起始是由SoundCloud公司开发的。随着发展&#xff0c;越来越多公司和组织接受采用Prometheus&#xff0c;社会也十分活跃&#xff0c;他们便…...

ESP32-Web-Server 实战编程-通过网页控制设备的 GPIO

ESP32-Web-Server 实战编程-通过网页控制设备的 GPIO 概述 前述博客讲解了 Web 编程的基本知识&#xff0c;包括 HTML、CSS、JavaScript 三个部分&#xff0c;从这节开始&#xff0c;我们进入实战部分&#xff0c;在实际项目中进一步学习 ESP32-Web 编程。 GPIO &#xff08…...

Springboot 中 指定 AspectJ 的织入模式

在Spring Boot中&#xff0c;AspectJ的织入模式可以通过以下两种方式进行明确指定&#xff1a; 使用配置文件&#xff08;application.properties或application.yml&#xff09;&#xff1a;在Spring Boot的配置文件中&#xff0c;可以添加以下属性来指定AspectJ的织入模式&am…...

【.NET全栈】.net的微软API接口与.NET框架源码

文章目录 0 前言1 微软官方.net接口学习2 .NET框架源码总结 0 前言 如果浏览器打不开链接&#xff0c;换一个浏览器打开。 我是 打不开微软的链接&#xff0c;使用&#xff1a; 可以打开&#xff01;&#xff01;&#xff01; 1 微软官方.net接口学习 https://docs.microsoft…...

【深度学习】基于深度学习的超分辨率图像技术一览

超分辨率(Super-Resolution)即通过硬件或软件的方法提高原有图像的分辨率&#xff0c;图像超分辨率是计算机视觉和图像处理领域一个非常重要的研究问题&#xff0c;在医疗图像分析、生物特征识别、视频监控与安全等实际场景中有着广泛的应用。 SR取得了显著进步。一般可以将现有…...

Android12强制所有应用跟随gsensor旋转

前言 Android12系统中如果机器带gsensor,竖屏应用如果固定了竖屏,当机器旋转为横屏,竖屏应用是不会转到横屏显示的,还是竖屏显示。抖音这种app就是这样的。因为app里面manifest文件中通过android:screenOrientation固定住了竖屏显示。如果要让横屏的时候app也能够横屏显示,…...

C#常用运算符的优先级

前言 运算符在C#编程语言中扮演着重要的角色&#xff0c;用于执行各种计算和操作。了解运算符的优先级是编写高效和正确代码的关键。本文将深入探讨C#中38个常用运算符的优先级划分和理解&#xff0c;并提供详细的说明和示例&#xff0c;以帮助读者更好地理解运算符的使用。 目…...

鸿蒙4.0开发笔记之ArkTS语法的基础数据类型[DevEco Studio开发](七)

文章目录 一、基本数据类型的定义1、变量声明2、数字类型3、字符串类型4、布尔类型5、数组类型6、元组类型7、枚举类型8、联合类型&#xff08;少用&#xff09;9、未知Unkown类型10、未定义和空值类型 二、数据类型的使用1、组件内部声明变量不需要使用let关键字2、使用Divide…...

集成学习的两种常见策略:bagging VS. boosting

chatGPT回答&#xff0c;记在这里。 集成学习是一种通过组合多个弱学习器来构建一个更强大的学习器的方法。其中&#xff0c;bagging和boosting是两种常见的集成学习策略。 一、bagging & boosting 简介 Bagging&#xff08;自助聚集法&#xff09;&#xff1a; Bagging…...

居家适老化设计第三十四条---卫生间之照明

居家适老化卫生间照明设计需要考虑以下几个方面&#xff1a;1. 光源选择&#xff1a;选择适合老年人眼睛的柔和光源&#xff0c;避免刺眼和眩光的发生。可以选择LED灯具&#xff0c;因为它们具有节能、寿命长和可调光的特点。2. 光线布置&#xff1a;在不同区域设置不同的光线&…...

如何使用Cloudreve将个人电脑打造为私有云盘并实现远程访问

文章目录 1、前言2、本地网站搭建2.1 环境使用2.2 支持组件选择2.3 网页安装2.4 测试和使用2.5 问题解决 3、本地网页发布3.1 cpolar云端设置3.2 cpolar本地设置 4、公网访问测试5、结语 1、前言 云存储概念兴起后&#xff0c;现在市面上也已经有了很多公有云盘。但一段时间后…...

[SaaS] 淘宝AI淘淘秀

AIGC技术在淘淘秀场景的探索与实践关键词&#xff1a;图像类AI创新应用、用户轻松创作、内容分享、结合商家品牌。https://mp.weixin.qq.com/s/-3a3_nKeKGON-9-Prd7JKQ 1.生成模版 利用定制的prompt&#xff0c;生成一些比较好的素材图片案例。 最终的用的是通义万相。 2.仿…...

第二证券:机构密集调研消费电子、半导体产业链

据上海证券报记者核算&#xff0c;近一个月来&#xff0c;共有41家消费电子类公司和92家半导体公司&#xff08;核算标准&#xff1a;申万职业2021&#xff0c;下同&#xff09;发布出资者调研纪要。其间&#xff0c;有的公司款待了16个批次估计超200家安排&#xff0c;更有公司…...

app小程序定制的重点|软件定制开发|网站搭建

app小程序定制的重点|软件定制开发|网站搭建 App小程序定制开发是近年来快速发展的一项技术服务&#xff0c;随着移动互联网的普及和用户需求的不断升级&#xff0c;越来越多的企业和个人开始关注和需求定制化的小程序开发。那么&#xff0c;对于app小程序定制开发来说&#xf…...

11-28渗透

用nmap扫描靶机1进行主机发现 已知靶机1的主机在172.16.17.0/24下 扫描结果如下 根据扫描结果看开启的服务怀疑172.16.17.177是靶机1 浏览器访问172.16.17.177页面得到如下 我们知道织梦cms系统默认管理路径是dede&#xff0c;登陆管理后台可以通过地址172.16.17.177/dede/i…...

qt实现一个安卓测试小工具

qt实现一个安卓测试小工具 最终效果&#xff1a;目录结构源码gui.py 主要是按钮&#xff0c;文本控制代码main.py 主要是逻辑代码gui.spec 是打包使用的adb.ui 打包为exe 最终效果&#xff1a; 目录结构 上面2个是打包的生成的不用管 源码 gui.py 主要是按钮&#xff0c;文…...

驾驭未来,智能化管理——汽车ERP系统

在汽车行业竞争日益激烈的今天&#xff0c;如何提高生产效率、优化供应链管理&#xff0c;确保产品质量和客户满意度成为汽车制造企业亟需解决的难题。为解决这一问题&#xff0c;汽车企业资源计划&#xff08;ERP&#xff09;系统应运而生。本文将为您介绍汽车ERP系统&#xf…...

flutter开发实战-当前界面无操作60s返回主页实现

flutter开发实战-当前界面无操作60s返回主页实现 当前界面无操作60s返回主页实现&#xff0c;主要是通过Timer来控制&#xff0c;当监听界面是否有pointerDown时候&#xff0c;如果超过60s仍没有操作&#xff0c;则返回主页。 一、Listener Listener是用来用于调用回调以响应…...

绩效考核的基础及基本内容

人力资源是企业的第一资源&#xff0c;员工绩效水平决定着人力资源价值的实现程度&#xff0c;绩效是企业永远的重点&#xff0c;没有绩效&#xff0c;一切无从谈起。很多企业在实施考核时扩大了绩效考核的积极作用&#xff0c;并没有考虑企业对绩效考核负面效应的承载能力&…...

阿坤老师的彩带插花(蓝桥杯)

阿坤老师的彩带插花 问题描述 阿坤老师是个充满创意的手工艺教师&#xff0c;他最近在教学生们制作彩带插花。每束彩带插花由多段彩带组成&#xff0c;每段彩带有左端和右端&#xff0c;左端到右端的长度不一。阿坤老师发现&#xff0c;有些彩带被完全插在了其他彩带之内&…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…...

2021-03-15 iview一些问题

1.iview 在使用tree组件时&#xff0c;发现没有set类的方法&#xff0c;只有get&#xff0c;那么要改变tree值&#xff0c;只能遍历treeData&#xff0c;递归修改treeData的checked&#xff0c;发现无法更改&#xff0c;原因在于check模式下&#xff0c;子元素的勾选状态跟父节…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作

一、上下文切换 即使单核CPU也可以进行多线程执行代码&#xff0c;CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短&#xff0c;所以CPU会不断地切换线程执行&#xff0c;从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

图表类系列各种样式PPT模版分享

图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

是否存在路径(FIFOBB算法)

题目描述 一个具有 n 个顶点e条边的无向图&#xff0c;该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序&#xff0c;确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数&#xff0c;分别表示n 和 e 的值&#xff08;1…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)

Aspose.PDF 限制绕过方案&#xff1a;Java 字节码技术实战分享&#xff08;仅供学习&#xff09; 一、Aspose.PDF 简介二、说明&#xff08;⚠️仅供学习与研究使用&#xff09;三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...

在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)

考察一般的三次多项式&#xff0c;以r为参数&#xff1a; p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]&#xff1b; 此多项式的根为&#xff1a; 尽管看起来这个多项式是特殊的&#xff0c;其实一般的三次多项式都是可以通过线性变换化为这个形式…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.

ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #&#xff1a…...

django blank 与 null的区别

1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是&#xff0c;要注意以下几点&#xff1a; Django的表单验证与null无关&#xff1a;null参数控制的是数据库层面字段是否可以为NULL&#xff0c;而blank参数控制的是Django表单验证时字…...