
文章目录1. 前言2. 前置工作3. 代码3.1 metrics.go3.2 main 方法3.3 项目启动4. 小结1. 前言最近工作开发的时候经常用到指标监控一般来说线上都会有调用链监控但是对比起来还是指标监控好用最重要的就是支持 PromQL语法有了语法就能查询出各种各样的指标比如每秒 QPS、每分钟最大耗时… 各种指标线上用的最多就是就是用来监控发送量、QPS下面来学习下 prometheus。2. 前置工作首先要下载一个Windows Prometheus这个工具是用来监控指标的当然用 go sdk 之后也可以在本地启动 http 接口查看指标但是查看的指标都是瞬时值也就是没有成图表这个工具就是用来看图表的。要下载可以到这个网站去下载https://prometheus.ac.cn/download/但是这个网站下载的太慢了我下载的时候速度基本是 几kb/s所以直接问豆包让豆包给一个百度网盘的地址来下载。下载下来之后解压到英文路径下别中文路径然后修改prometheus.yml。global:scrape_interval:15sscrape_configs:-job_name:prometheusstatic_configs:-targets:[127.0.0.1:8088]来解释下上面配置的意思就是 prometheus 会每隔 15s 到本地拉取一次指标没有配置metrics_path那么默认会访问http://127.0.0.1:8088/metrics端口所以我们在项目里面也要暴露 metrics 端口方便指标拉取。接下来到解压目录下创建一个 bat 执行文件prometheus.bat。echo off chcp 65001 title Prometheus监控服务 :: 切换到当前脚本所在目录 cd /d %~dp0 echo 正在启动 Prometheus... prometheus.exe --config.fileprometheus.yml pause这个脚本很简单核心就是调用prometheus.exe --config.fileprometheus.yml这行命令启动 prometheus启动之后通过 127.0.0.1:9090打开网站prometheus 默认监听9090端口。注意把左上角的Use local time点上不然时间不对得8。3. 代码好了上面准备好 prometheus 之后下面就是具体代码引入github.com/prometheus/client_golang v1.20.5这个关键包然后看下下面几个方法方法指标类型用法prometheus.NewCounter(...)Counter用于只增不减的累计值比如请求总数、错误总数、处理总数prometheus.NewGauge(...)Gauge用于可增可减的当前值比如并发请求数、活跃用户数、队列深度prometheus.NewHistogram(...)Histogram用于统计耗时、大小等分布数据prometheus.NewHistogramVec(...)HistogramVec用于按标签维度统计耗时、大小等分布数据prometheus.NewRegistry()Registry用于创建自定义注册表并统一注册和暴露指标这几个方法中前 4 个就是创建指标最后一个是创建注册表用于注册指标同时暴露指标给上面的 prometheus 组件读取。3.1 metrics.go下面看下这个文件这个文件就是用来初始化指标的先来看下指标。typeRegistrystruct{RequestsTotal prometheus.Counter RequestDuration prometheus.Histogram RequestDurationByMethod*prometheus.HistogramVec InFlightRequests prometheus.Gauge JobLastRunTimestamp prometheus.Gauge JobRunTotal prometheus.Counter JobFailureTotal prometheus.Counter ActiveUsers prometheus.Gauge queueDepth prometheus.Gauge queueProcessedTotal prometheus.Counter registry*prometheus.Registry// 启动时间startedAtint64}然后就是这些指标的初始化。funcNewRegistry()*Registry{reg:prometheus.NewRegistry()r:Registry{// RequestsTotal 统计服务处理过的 HTTP 请求总数RequestsTotal:prometheus.NewCounter(prometheus.CounterOpts{Name:demo_http_requests_total,Help:Total number of HTTP requests handled by the demo service.,}),// RequestDuration 统计所有 HTTP 请求的耗时分布RequestDuration:prometheus.NewHistogram(prometheus.HistogramOpts{Name:demo_http_request_duration_seconds,Help:HTTP request duration in seconds.,Buckets:prometheus.DefBuckets,}),// RequestDurationByMethod 按请求方法统计 HTTP 请求耗时分布RequestDurationByMethod:prometheus.NewHistogramVec(prometheus.HistogramOpts{Name:demo_http_request_duration_by_method_seconds,Help:HTTP request duration in seconds partitioned by request method.,Buckets:prometheus.DefBuckets,},[]string{method},),// InFlightRequests 统计当前正在处理中的请求数InFlightRequests:prometheus.NewGauge(prometheus.GaugeOpts{Name:demo_http_in_flight_requests,Help:Current number of in-flight requests.,}),// JobLastRunTimestamp 记录后台任务最近一次执行的时间戳JobLastRunTimestamp:prometheus.NewGauge(prometheus.GaugeOpts{Name:demo_job_last_run_timestamp_seconds,Help:Unix timestamp of the last background job run.,}),// JobRunTotal 统计后台任务执行的总次数JobRunTotal:prometheus.NewCounter(prometheus.CounterOpts{Name:demo_job_runs_total,Help:Total number of background job runs.,}),// JobFailureTotal 统计后台任务执行失败的总次数JobFailureTotal:prometheus.NewCounter(prometheus.CounterOpts{Name:demo_job_failures_total,Help:Total number of simulated background job failures.,}),// ActiveUsers 表示当前模拟的活跃用户数ActiveUsers:prometheus.NewGauge(prometheus.GaugeOpts{Name:demo_active_users,Help:Current simulated active users.,}),// queueDepth 表示当前模拟的队列积压深度queueDepth:prometheus.NewGauge(prometheus.GaugeOpts{Name:demo_queue_depth,Help:Current simulated queue depth.,}),// queueProcessedTotal 统计队列累计处理完成的任务数queueProcessedTotal:prometheus.NewCounter(prometheus.CounterOpts{Name:demo_queue_processed_total,Help:Total number of simulated queue items processed.,}),registry:reg,startedAt:time.Now().Unix(),}reg.MustRegister(r.RequestsTotal,r.RequestDuration,r.RequestDurationByMethod,r.InFlightRequests,r.JobLastRunTimestamp,r.JobRunTotal,r.JobFailureTotal,r.ActiveUsers,r.queueDepth,r.queueProcessedTotal,)returnr}上面就是初始化指标的代码里面的属性先不解释最终指标注册到注册中心是通过MustRegister方法批量注册的下面看下这些指标的更新。// ObserveRequest 请求统计增加func(r*Registry)ObserveRequest(duration time.Duration){// 总请求数 1r.RequestsTotal.Inc()// 请求耗时r.RequestDuration.Observe(duration.Seconds())}// ObserveRequestByMethod 统计每个方法的耗时func(r*Registry)ObserveRequestByMethod(methodstring,duration time.Duration){r.RequestDurationByMethod.WithLabelValues(method).Observe(duration.Seconds())}// SetActiveUsers 设置当前活跃的用户func(r*Registry)SetActiveUsers(nint){r.ActiveUsers.Set(float64(n))}// SetQueueDepth 设置当前队列里还堆着多少任务没处理func(r*Registry)SetQueueDepth(nint){r.queueDepth.Set(float64(n))}// AddProcessedQueue 设置队列累积完成的任务数func(r*Registry)AddProcessedQueue(nint){r.queueProcessedTotal.Add(float64(n))}// MarkJobRunfunc(r*Registry)MarkJobRun(successbool){// 定时任务执行次数r.JobRunTotal.Inc()// 定时任务上一次执行的时间r.JobLastRunTimestamp.Set(float64(time.Now().Unix()))if!success{// 定时任务执行失败的次数r.JobFailureTotal.Inc()}}func(r*Registry)StartedAt()int64{returnatomic.LoadInt64(r.startedAt)}func(r*Registry)RouteCount(countint)string{returnstrconv.Itoa(count)}最后就是metrics路径的处理器也就是 prometheus 读取本地指标接口的处理器。func(r*Registry)Handler()http.Handler{returnpromhttp.HandlerFor(r.registry,promhttp.HandlerOpts{})}3.2 main 方法packagemainimport(fmtlogmath/randnet/httptimeexample.com/prometheus-1/metrics)funcmain(){r:metrics.NewRegistry()// 模拟后台定时任务gorunBackgroundJob(r)// 收到请求就调用下面的来处理http.HandleFunc(/,func(w http.ResponseWriter,req*http.Request){// 当前时间start:time.Now()// 当前正在处理的请求数 1r.InFlightRequests.Inc()deferfunc(){// 当前正在处理的请求数 -1r.InFlightRequests.Dec()// 方法耗时duration:time.Since(start)// 已经处理的请求数 1r.ObserveRequest(duration)// 统计已经处理过的方法请求耗时r.ObserveRequestByMethod(req.Method,duration)}()// 模拟当前活跃用户数users:50rand.Intn(200)queue:rand.Intn(100)r.SetActiveUsers(users)r.SetQueueDepth(queue)// 模拟设置队列累积完成的任务数r.AddProcessedQueue(1rand.Intn(8))_,_fmt.Fprintf(w,hello prometheus, users%d queue%d\n,users,queue)})http.HandleFunc(/work,func(w http.ResponseWriter,req*http.Request){// 当前时间start:time.Now()// 当前正在处理的请求数 1r.InFlightRequests.Inc()deferfunc(){r.InFlightRequests.Dec()duration:time.Since(start)r.ObserveRequest(duration)r.ObserveRequestByMethod(req.Method,duration)}()cost:20rand.Intn(80)time.Sleep(time.Duration(cost)*time.Millisecond)r.SetActiveUsers(100rand.Intn(300))r.SetQueueDepth(rand.Intn(50))r.AddProcessedQueue(5)_,_fmt.Fprintf(w,work done, cost%dms\n,cost)})http.HandleFunc(/healthz,func(w http.ResponseWriter,req*http.Request){_,_w.Write([]byte(ok))})http.Handle(/metrics,r.Handler())addr::8088log.Printf(prometheus-1 listening on %s,addr)log.Printf(metrics endpoint: http://127.0.0.1%s/metrics,addr)log.Fatal(http.ListenAndServe(addr,nil))}funcrunBackgroundJob(r*metrics.Registry){ticker:time.NewTicker(5*time.Second)deferticker.Stop()forrangeticker.C{success:rand.Intn(10)!0r.MarkJobRun(success)}}funcinit(){rand.Seed(time.Now().UnixNano())}3.3 项目启动接下来启动项目输出如下。2026/05/1600:06:08prometheus-1listening on:80882026/05/1600:06:08metrics endpoint:http://127.0.0.1:8088/metrics我们在浏览器输入http://127.0.0.1:8088/metrics可以访问到指标。接下来我们访问http://localhost:8088/work接口以及http://localhost:8088然后在 prometheus 界面查看指标下面随便找几个指标来看首先是rate(demo_http_requests_total[1m])这个指标用于计算每秒的请求量rate就是平均的意思。然后就是每个类型的方法的 QPS查看全部类型的方法总和的 QPS 可以用这个指令rate(demo_http_request_duration_by_method_seconds_count[1m])又或者用下面的sum(rate(demo_http_request_duration_by_method_seconds_count[1m])) by (method)来查看每个方法的 QPS。接下来就是查看请求平均耗时统计时间是 1min 内(rate(demo_http_request_duration_seconds_sum[1m]) / rate(demo_http_request_duration_seconds_count[1m])) * 1000。除了平均耗时生产环境还有一个指标就是 p95 耗时也就是通过这个指标(histogram_quantile(0.95,sum(rate(demo_http_request_duration_by_method_seconds_bucket[1m])) by (method, le))) * 1000这个指标会按照Get、Post等方法去分组分别计算出1 分钟内的 p95 平均耗时。下面顺便再说下我们会看到指标后面有一个le这个指标的意思就是小于x秒的请求数有多少这个是 NewHistogram 才有的。最后我们再来看最大值生产环境下需要监控某个方法的最大值用来确认这个方法的耗时情况比如说如果发现整个链路有什么地方堵住了一般来说就先看平均耗时和最大耗时确认是不是某个方法的耗时增加了再确认这个方法内部哪个地方耗时增加。由于我们没有单独的最大值耗时指标所以可以用另外的方法去计算首先通过sum(rate(demo_http_request_duration_by_method_seconds_bucket[1m])) by (le, method)算出 1 分钟内每秒处理的方法数按照方法和 le 指标分组曲线是这样的。接下来就可以通过histogram_quantile(1, sum(rate(demo_http_request_duration_by_method_seconds_bucket[5m])) by (le, method))计算出上面的全部方法100% 在多少 s 内完成。4. 小结好了这篇文章就先学习到这里简单学习了下 prometheus 指标的使用。如有错误欢迎指出