背景
基于前面的垃圾回收器的理论的学习,我们基本掌握了垃圾回收器的原理。但是这还不够,在实际的面试里,我们经常被问到的一个问题就是,"你说一下JVM调优思路"。为了弄明白JVM调优的思路,我们当然要实际操作一把,但是在真正开始调优之前,我们先学习一个新的技术,prometheus,它是我们接下来要学习JVM调优必须具备的技术。
prometheus简介
简单概括prometheus是一个监控系统,能监控系统的各个指标。比如说我想知道一个接口的平均耗时是多少,最近一个小时内请求了多少次,成功了多少次。我需要一个系统能统计这个数据,并且把它展示出来,那么prometheus就是很好的解决方案。
当然prometheus之所以强大不仅仅是因为它功能强大,还因为它的生态强大。比如prometheus统计,聚合,查询服务器数据的能力很强,但是展示并不友好(实际上prometheus原生的展示界面非常简陋),但是它可以和Grafana结合来展示数据。当我们的服务器发生异常时,我们希望根据prometheus的指标判断出来并且发出报警通知到我们,那么我们还可以使用AlertManager来配合prometheus发出告警。
总之prometheus是一套很强大的监控解决方案,大家记住这句话即可。
环境搭建
为了方便大家学习,我这里讲解一下怎么搭建虚拟机下prometheus单机环境。搭建环境是centos7
prometheus安装配置
下载prometheus安装包。官网地址prometheus.io/download/。不会魔法的朋友可能打不开,不过我在文章末尾放出了所有的安装包资源,大家可以自取。

为了方便我们管理,这里我设计一个统一的管理目录/opt/data/soft,方便后续一起管理其他软件。
下载完毕后,拷贝到/opt/data/soft/prometheus,
bash
# 创建目录
mkdir -p /opt/data/soft/prometheus
# 解压
tar -zxvf prometheus-2.49.1.linux-amd64.tar.gz
# 解压后进到解压目录里,尝试启动prometheus看看安装包坏没坏
./prometheus --storage.tsdb.path="/opt/data/soft/prometheus/data" --log.level=debug --web.enable-lifecycle --web.enable-admin-api
启动后访问网址看看页面能不能打开,这决定了后续数据能不能正确上报。
IP换成自己的IP。如果打不开,搜索centos7端口打不开的解决办法,比如关闭firewalld或者iptables。云服务器可能还要额外的设置,这个大家自视情况而定。
正常大家会看到这个页面,说明启动成功了。

grafana安装配置
prometheus的原生界面展示的指标非常简陋,我们用grafana来展示prometheus的指标。去到官网下载grafana.com/grafana/dow...下载地址

下载后复制到/opt/data/soft/grafana目录,然后解压。命令如下
bash
## 不能下载的可以在文章末尾找到我下好的软甲包
wget https://dl.grafana.com/enterprise/release/grafana-enterprise-10.2.3.linux-amd64.tar.gz
tar -zxvf grafana-enterprise-10.2.3.linux-amd64.tar.gz
#进到软件目录
#复制出一份配置文件,不需要更改里面的配置
cp conf/sample.ini conf/grafana.ini
#启动命令
./bin/grafana-server -config conf/grafana.ini
启动后打开网页http://192.168.0.150:3000/login,应该能看到grafana的界面.grafana的默认用户密码都是admin,第一次登录会要求修改密码。我们为了方便直接还是改为admin

进入grafana之后

SpringBoot项目整合Prometheus
有了环境,我们还要有展示的指标,所以这里我搭建一个示例项目用来展示prometheus的指标用法。
maven依赖
xml
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
application配置
yaml
management:
endpoints:
web:
base-path: /actuator
exposure:
include: prometheus
server:
port: 8113
metrics:
export:
prometheus:
enabled: true
tags:
application: ${spring.application.name}
然后启动服务,访问接口http://192.168.66.120:8113/actuator/prometheus,如果能看到展示的指标说明配置正确。
这里我们就搭建起来了prometheus的使用环境,具体的指标用法在后面。

prometheus配置拉指标数据源
peometheus会定期去拉取服务器的指标数据,所以我们还要在prometheus上配置需要拉取指标的服务地址。
去到prometheus的安装目录,在prometheus.yml文件里更改配置,添加我们要采集的spring服务地址
yaml
global:
scrape_interval: 5s # 全局的采集数据周期
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
scrape_timeout: 15s # 全局的采集超时时间
scrape_configs:
- job_name: "prometheus-demo"
scrape_interval: 10s #本采集任务的采集数据周期
scrape_timeout: 20s #本采集任务的超时时间
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['192.168.66.120:8113']
拉取成功后打开prometheus的web界面,找到我们配置的spring服务,看是不是成功被采集到了

prometheus语法
指标的格式
下面是一个指标的典型格式。custom_message_length_total是指标名字,大括号内的内容被称为labels,labels内有3个标签,同样的指标名字,标签不一样,也是不一样的指标。最后的5.0表示指标的值。
ini
custom_message_length_total{application="prometheus_study",message_length="10",message_time="2024-01-20",} 5.0
prometheus有4中指标,我们分别介绍
Counter
Counter是一个只增不减的指标,可以用counter来记录服务的请求数,已完成的任务数,发生错误的次数等。通过labels指定标签,inc方法增加次数,或者add(float)增加指定次数。
一个label指标例子。我们统计一天收到消息条数,并根据消息的长度做区分。

指标格式
ini
custom_message_length_total{application="prometheus_study",message_length="5",message_time="2024-01-20",} 4.0
访问spring的指标暴露接口 http://192.168.66.120:8113/actuator/prometheus
可以看到我们的自定义的指标

Gauge
仪表盘代表一种可以随意变化的指标,比如内存使用率这种指标,或者并发请求数量。也是使用labels来指定标签名称。
注意这个值时随时间变化的,而且两次记录之间有可能丢失数据,随着时间周期的粒度变大,丢失关键变化的情况会增多。
我们设计一个队列存储所有收到的消息。然后用guage记录队列里消息的条数。

从http://192.168.66.120:8113/actuator/prometheus 里查看我们队列里消息的条数,显示有5条。

我们发送一条remove指令,然后发现队列里消息的条数变少了

Histogram
直方图,它其实是将指标按照一定的规则分组后的指标。label中的标签就是分组的规则。每个创建的指标后面就会存在一个"指标名__histogram"的指标,表示分组后的结果。
这里我用Histogram来统计接口耗时

可以看到,统计出耗时在50ms以下的请求有1个,100ms以下有2个,200ms以下5个,500ms以下12个,1000ms以下17个。

细心的读者肯定发现了,除了HistogramName_Hitogram指标外,还有 HistogramName_seconds_bucket这种指标。这两者其实是一个指标,只是表示形式不一样,大家可以对照着看指标值是不是都一样。 HistogramName_Hitogram是把每个labels对应的标签分配进bucket,bucket分组就是我们设置的分组规则。实际记录的值除了每个HistogramName_bucket指标外还存在HistogramName_count和HistogramName_sum,分别表示所有观测到的事件总次数和观测到的事件的值的和。在HistogramName_bucket中当上界le为+Inf时,它的值为所有接受到的事件次数,这个结果就和HistogramName_Count一样了。
Summary
Summary的含义和Histogram表达的含义一样,都是对指标进行聚合后的展示。但是Summary在一些计算时会直接在客户端计算,而Histogram则是在服务端计算,比如计算一个指标的中位数。

可以看到Summary指标和Histogram指标的统计结果完全一样

PromQL
我们的指标其实是存储在prometheus服务器上的,以上所有指标都有对应的函数,和语法配合函数可以更准确的展示出对应的数据变化规律。专门操作prometheus指标的语法就是PromQL。
接下来我们介绍如何使用PromQL更好的展示指标。
我们知道custom_message_send_cost_time_histogram指标代表每个耗时阶段的请求个数。比如这里耗时200ms以下的请求1个,耗时500ms以下的请求7个,那么耗时100ms - 500ms之间的请求有多少个呢,虽然我们口算也可以,但是数据量很大时明显就不能口算了。我们用PromQL计算一下。

去到http://192.168.66.69:9090/graph输入计算函数。
ini
scalar(custom_message_send_cost_time_histogram{application="prometheus_study",le="0.5",})-scalar(custom_message_send_cost_time_histogram{application="prometheus_study",le="0.2",})
可以看到展示的计算结果是6

PromQL的功能很强大,函数也很丰富,大家感兴趣自己在去了解。
Grafana接入prometheus
上面我们可以看到,prometheus展示的界面太丑了,我们一开始介绍了grafana是用来替换prometheus展示数据的,这里介绍下怎么用Grafana接入Prometheus。
接入prometheus数据源
进入主界面选择接入prometheus数据源

选创建新数据源

填好数据源名字和prometheus的访问地址后保存

创建自定义面板
创建新看板


数据源选我们刚才新建的prometheus-demo

还要再选一遍数据源

我这里选择我们创建的记录接口耗时的指标custom_message_send_cost_time_histogram

label里的标签也得选,然后预览。然后保存


到这里,我们自己创建的指标就可以展示了。最终结果

导入预设面板
从上面的创建步骤来看,创建一个面板还是不简单,而且如果我们要用一些PromQL来创建面板时,那步骤会更加复杂。而且我们的Spring项目有那么多指标,总不能全部都由我们新建吧,所以我们还要学会导入其他人开发好的面板。
好在Spring有非常好的生态,社区里已经有人建好了一套Spring程序的面板,里面包含了大部分常用的监控指标,我们可以直接导入。
模板官网 grafana.com/grafana/das...
找到任意的我们想要下载的模板,下载JSON配置

在看板界面选择导入我们想要的看板

导入时数据源选择我们创建的prometheus-demo

保存后就能看到我们想要的看板

这里我推荐一个看板4701,这个看板是监控JVM信息的,用我们配置的Spring项目就可以展示。
这个面板非常全面,是我们后续讲JVM调优实践必须要用的。我替大大家下好了。

还有一些监控服务器状态的看板,不过需要安装额外的指标采集程序,我就不演示了。也不是重点,大家知道就行了。
END
到这里关于prometheus的知识就讲完了,prometheus是后面JVM调优实施的必要条件,大家一定要掌握。另外本文所有用到的资源,配置,以及代码我已经打包好了,大家在不会魔法就自己下载然后照着操作就行。
资源位置,公众号回复8110