一、监控的意义
对于现代的互联网程序来说,规模越来越大,功能越来越复杂,还要追求更好的客户体验,因此要监控的信息量也就比较大了。由于现在的互联网程序大部分都是基于微服务的程序,一个程序的运行需要若干个服务来保障,因此第一个要监控的指标就是服务是否正常运行,也就是监控服务状态是否处理宕机状态。
那具体的监控要如何开展呢?还要从实际的程序运行角度出发。比如现在有3个服务支撑着一个程序的运行,每个服务都有自己的运行状态。
此时被监控的信息就要在三个不同的程序中去查询并展示,但是三个服务是服务于一个程序的运行的,如果不能合并到一个平台上展示,监控工作量巨大,而且信息对称性差,要不停的在三个监控端查看数据。如果将业务放大成30个,300个,3000个呢?看来必须有一个单独的平台,将多个被监控的服务对应的监控指标信息汇总在一起,这样更利于监控工作的开展。
新的程序专门用来监控,新的问题就出现了,是被监控程序主动上报信息还是监控程序主动获取信息?如果监控程序不能主动获取信息,这就意味着监控程序有可能看到的是很久之前被监控程序上报的信息,万一被监控程序宕机了,监控程序就无法区分究竟是好久没法信息了,还是已经下线了。所以监控程序必须具有主动发起请求获取被监控服务信息的能力。
如果监控程序要监控服务时,主动获取对方的信息。那监控程序如何知道哪些程序被自己监控呢?不可能在监控程序中设置我监控谁,这样互联网上的所有程序岂不是都可以被监控到,这样的话信息安全将无法得到保障。合理的做法只能是在被监控程序启动时上报监控程序,告诉监控程序你可以监控我了。看来需要在被监控程序端做主动上报的操作,这就要求被监控程序中配置对应的监控程序是谁。
被监控程序可以提供各种各样的指标数据给监控程序看,但是每一个指标都代表着公司的机密信息,并不是所有的指标都可以给任何人看的,乃至运维人员,所以对被监控指标的是否开放出来给监控系统看,也需要做详细的设定。
以上描述的整个过程就是一个监控系统的基本流程。
二、可视化监控平台
springboot抽取了大部分监控系统的常用指标,提出了监控的总思想。然后就有好心的同志根据监控的总思想,制作了一个通用性很强的监控系统,因为是基于springboot监控的核心思想制作的,所以这个程序被命名为Spring Boot Admin。
Spring Boot Admin,这是一个开源社区项目,用于管理和监控SpringBoot应用程序。这个项目中包含有客户端和服务端两部分,而监控平台指的就是服务端。我们做的程序如果需要被监控,将我们做的程序制作成客户端,然后配置服务端地址后,服务端就可以通过HTTP请求的方式从客户端获取对应的信息,并通过UI界面展示对应信息。
下面就来开发这套监控程序,先制作服务端,其实服务端可以理解为是一个web程序,收到一些信息后展示这些信息。
服务端开发
步骤①:导入springboot admin对应的starter,版本与当前使用的springboot版本保持一致,并将其配置成web工程
<dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-server</artifactId> <version>2.5.4</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
上述过程可以通过创建项目时使用勾选的形式完成。
步骤②:在引导类上添加注解@EnableAdminServer,声明当前应用启动后作为SpringBootAdmin的服务器使用
@SpringBootApplication @EnableAdminServer public class Springboot25AdminServerApplication { public static void main(String[] args) { SpringApplication.run(Springboot25AdminServerApplication.class, args); } }
做到这里,这个服务器就开发好了,启动后就可以访问当前程序了,界面如下。
由于目前没有启动任何被监控的程序,所以里面什么信息都没有。下面制作一个被监控的客户端程序。
客户端开发
客户端程序开发其实和服务端开发思路基本相似,多了一些配置而已。
步骤①:导入springboot admin对应的starter,版本与当前使用的springboot版本保持一致,并将其配置成web工程
<dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-client</artifactId> <version>2.5.4</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
上述过程也可以通过创建项目时使用勾选的形式完成,不过一定要小心,端口配置成不一样的,否则会冲突。
步骤②:设置当前客户端将信息上传到哪个服务器上,通过yml文件配置
spring:
boot:
admin:
client:
url: http://localhost:8080
做到这里,这个客户端就可以启动了。启动后再次访问服务端程序,界面如下。
可以看到,当前监控了1个程序,点击进去查看详细信息。
由于当前没有设置开放哪些信息给监控服务器,所以目前看不到什么有效的信息。下面需要做两组配置就可以看到信息了。
开放指定信息给服务器看
允许服务器以HTTP请求的方式获取对应的信息
配置如下:
server: port: 80 spring: boot: admin: client: url: http://localhost:8080 management: endpoint: health: show-details: always endpoints: web: exposure: include: "*"
上述配置对于初学者来说比较容易混淆。简单解释一下,到下一节再做具体的讲解。springbootadmin的客户端默认开放了13组信息给服务器,但是这些信息除了一个之外,其他的信息都不让通过HTTP请求查看。所以你看到的信息基本上就没什么内容了,只能看到一个内容,就是下面的健康信息。
但是即便如此我们看到健康信息中也没什么内容,原因在于健康信息中有一些信息描述了你当前应用使用了什么技术等信息,如果无脑的对外暴露功能会有安全隐患。通过配置就可以开放所有的健康信息明细查看了。
management: endpoint: health: show-details: always
健康明细信息如下:
目前除了健康信息,其他信息都查阅不了。原因在于其他12种信息是默认不提供给服务器通过HTTP请求查阅的,所以需要开启查阅的内容项,使用*表示查阅全部。记得带引号。
endpoints: web: exposure: include: "*"
配置后再刷新服务器页面,就可以看到所有的信息了。
以上界面中展示的信息量就非常大了,包含了13组信息,有性能指标监控,加载的bean列表,加载的系统属性,日志的显示控制等等。
配置多个客户端
可以通过配置客户端的方式在其他的springboot程序中添加客户端坐标,这样当前服务器就可以监控多个客户端程序了。每个客户端展示不同的监控信息。
进入监控面板,如果你加载的应用具有功能,在监控面板中可以看到3组信息展示的与之前加载的空工程不一样。
类加载面板中可以查阅到开发者自定义的类,如左图
映射中可以查阅到当前应用配置的所有请求
性能指标中可以查阅当前应用独有的请求路径统计数据
三、监控原理
通过查阅监控中的映射指标,可以看到当前系统中可以运行的所有请求路径,其中大部分路径以/actuator开头
首先这些请求路径不是开发者自己编写的,其次这个路径代表什么含义呢?既然这个路径可以访问,就可以通过浏览器发送该请求看看究竟可以得到什么信息。
通过发送请求,可以得到一组json信息,如下
{ "_links": { "self": { "href": "http://localhost:81/actuator", "templated": false }, "beans": { "href": "http://localhost:81/actuator/beans", "templated": false }, "caches-cache": { "href": "http://localhost:81/actuator/caches/{cache}", "templated": true }, "caches": { "href": "http://localhost:81/actuator/caches", "templated": false }, "health": { "href": "http://localhost:81/actuator/health", "templated": false }, "health-path": { "href": "http://localhost:81/actuator/health/{*path}", "templated": true }, "info": { "href": "http://localhost:81/actuator/info", "templated": false }, "conditions": { "href": "http://localhost:81/actuator/conditions", "templated": false }, "shutdown": { "href": "http://localhost:81/actuator/shutdown", "templated": false }, "configprops": { "href": "http://localhost:81/actuator/configprops", "templated": false }, "configprops-prefix": { "href": "http://localhost:81/actuator/configprops/{prefix}", "templated": true }, "env": { "href": "http://localhost:81/actuator/env", "templated": false }, "env-toMatch": { "href": "http://localhost:81/actuator/env/{toMatch}", "templated": true }, "loggers": { "href": "http://localhost:81/actuator/loggers", "templated": false }, "loggers-name": { "href": "http://localhost:81/actuator/loggers/{name}", "templated": true }, "heapdump": { "href": "http://localhost:81/actuator/heapdump", "templated": false }, "threaddump": { "href": "http://localhost:81/actuator/threaddump", "templated": false }, "metrics-requiredMetricName": { "href": "http://localhost:81/actuator/metrics/{requiredMetricName}", "templated": true }, "metrics": { "href": "http://localhost:81/actuator/metrics", "templated": false }, "scheduledtasks": { "href": "http://localhost:81/actuator/scheduledtasks", "templated": false }, "mappings": { "href": "http://localhost:81/actuator/mappings", "templated": false } } }
其中每一组数据都有一个请求路径,而在这里请求路径中有之前看到过的health,发送此请求又得到了一组信息
{ "status": "UP", "components": { "diskSpace": { "status": "UP", "details": { "total": 297042808832, "free": 72284409856, "threshold": 10485760, "exists": true } }, "ping": { "status": "UP" } } }
当前信息与监控面板中的数据存在着对应关系
原来监控中显示的信息实际上是通过发送请求后得到json数据,然后展示出来。按照上述操作,可以发送更多的以/actuator开头的链接地址,获取更多的数据,这些数据汇总到一起组成了监控平台显示的所有数据。
到这里我们得到了一个核心信息,监控平台中显示的信息实际上是通过对被监控的应用发送请求得到的。那这些请求谁开发的呢?打开被监控应用的pom文件,其中导入了springboot admin的对应的client,在这个资源中导入了一个名称叫做actuator的包。被监控的应用之所以可以对外提供上述请求路径,就是因为添加了这个包。
这个actuator是什么呢?这就是本节要讲的核心内容,监控的端点。
Actuator,可以称为端点,描述了一组监控信息,SpringBootAdmin提供了多个内置端点,通过访问端点就可以获取对应的监控信息,也可以根据需要自定义端点信息。通过发送请求路劲**/actuator可以访问应用所有端点信息,如果端点中还有明细信息可以发送请求/actuator/端点名称**来获取详细信息。以下列出了所有端点信息说明:
ID 描述 默认启用 auditevents 暴露当前应用程序的审计事件信息。 是 beans 显示应用程序中所有 Spring bean 的完整列表。 是 caches 暴露可用的缓存。 是 conditions 显示在配置和自动配置类上评估的条件以及它们匹配或不匹配的原因。 是 configprops 显示所有 @ConfigurationProperties 的校对清单。 是 env 暴露 Spring ConfigurableEnvironment 中的属性。 是 flyway 显示已应用的 Flyway 数据库迁移。 是 health 显示应用程序健康信息 是 httptrace 显示 HTTP 追踪信息(默认情况下,最后 100 个 HTTP 请求/响应交换)。 是 info 显示应用程序信息。 是 integrationgraph 显示 Spring Integration 图。 是 loggers 显示和修改应用程序中日志记录器的配置。 是 liquibase 显示已应用的 Liquibase 数据库迁移。 是 metrics 显示当前应用程序的指标度量信息。 是 mappings 显示所有 @RequestMapping 路径的整理清单。 是 scheduledtasks 显示应用程序中的调度任务。 是 sessions 允许从 Spring Session 支持的会话存储中检索和删除用户会话。当使用 Spring Session 的响应式 Web 应 用程序支持时不可用。 是 shutdown 正常关闭应用程序。 否 threaddump 执行线程 dump。 是 heapdump 返回一个 hprof 堆 dump 文件。 是 jolokia 通过 HTTP 暴露 JMX bean(当 Jolokia 在 classpath 上时,不适用于 WebFlux)。 是 logfile 返回日志文件的内容(如果已设置 logging.file 或 logging.path 属性)。支持使用 HTTP Range 头来检索部分日志文件的内容。 是 prometheus 以可以由 Prometheus 服务器抓取的格式暴露指标。 是
上述端点每一项代表被监控的指标,如果对外开放则监控平台可以查询到对应的端点信息,如果未开放则无法查询对应的端点信息。通过配置可以设置端点是否对外开放功能。使用enable属性控制端点是否对外开放。其中health端点为默认端点,不能关闭。
management: endpoint: health: # 端点名称 show-details: always info: # 端点名称 enabled: true # 是否开放
为了方便开发者快速配置端点,springboot admin设置了13个较为常用的端点作为默认开放的端点,如果需要控制默认开放的端点的开放状态,可以通过配置设置,如下:
management: endpoints: enabled-by-default: true # 是否开启默认端点,默认值true
上述端点开启后,就可以通过端点对应的路径查看对应的信息了。但是此时还不能通过HTTP请求查询此信息,还需要开启通过HTTP请求查询的端点名称,使用“*”可以简化配置成开放所有端点的WEB端HTTP请求权限。
management: endpoints: web: exposure: include: "*"
整体上来说,对于端点的配置有两组信息,一组是endpoints开头的,对所有端点进行配置,一组是endpoint开头的,对具体端点进行配置。
management: endpoint: # 具体端点的配置 health: show-details: always info: enabled: true endpoints: # 全部端点的配置 web: exposure: include: "*" enabled-by-default: true
四、自定义监控指标
端点描述了被监控的信息,除了系统默认的指标,还可以自行添加显示的指标,下面就通过3种不同的端点的指标自定义方式来学习端点信息的二次开发。
INFO端点
info端点描述了当前应用的基本信息,可以通过两种形式快速配置info端点的信息
配置形式
在yml文件中通过设置info节点的信息就可以快速配置端点信息
info: appName: @project.artifactId@ version: @project.version@ company: 传智教育 author: itheima
配置完毕后,对应信息显示在监控平台上
也可以通过请求端点信息路径获取对应json信息
编程形式
通过配置的形式只能添加固定的数据,如果需要动态数据还可以通过配置bean的方式为info端点添加信息,此信息与配置信息共存
@Component public class InfoConfig implements InfoContributor { @Override public void contribute(Info.Builder builder) { builder.withDetail("runTime",System.currentTimeMillis()); //添加单个信息 Map infoMap = new HashMap(); infoMap.put("buildTime","2006"); builder.withDetails(infoMap); //添加一组信息 } }
Health端点
health端点描述当前应用的运行健康指标,即应用的运行是否成功。通过编程的形式可以扩展指标信息。
@Component public class HealthConfig extends AbstractHealthIndicator { @Override protected void doHealthCheck(Health.Builder builder) throws Exception { boolean condition = true; if(condition) { builder.status(Status.UP); //设置运行状态为启动状态 builder.withDetail("runTime", System.currentTimeMillis()); Map infoMap = new HashMap(); infoMap.put("buildTime", "2006"); builder.withDetails(infoMap); }else{ builder.status(Status.OUT_OF_SERVICE); //设置运行状态为不在服务状态 builder.withDetail("上线了吗?","你做梦"); } } }
当任意一个组件状态不为UP时,整体应用对外服务状态为非UP状态。
Metrics端点
metrics端点描述了性能指标,除了系统自带的监控性能指标,还可以自定义性能指标。
@Service public class BookServiceImpl extends ServiceImpl<BookDao, Book> implements IBookService { @Autowired private BookDao bookDao; private Counter counter; public BookServiceImpl(MeterRegistry meterRegistry){ counter = meterRegistry.counter("用户付费操作次数:"); } @Override public boolean delete(Integer id) { //每次执行删除业务等同于执行了付费业务 counter.increment(); return bookDao.deleteById(id) > 0; } }
在性能指标中就出现了自定义的性能指标监控项
自定义端点
可以根据业务需要自定义端点,方便业务监控
@Component @Endpoint(id="pay",enableByDefault = true) public class PayEndpoint { @ReadOperation public Object getPay(){ Map payMap = new HashMap(); payMap.put("level 1","300"); payMap.put("level 2","291"); payMap.put("level 3","666"); return payMap; } }
由于此端点数据spirng boot admin无法预知该如何展示,所以通过界面无法看到此数据,通过HTTP请求路径可以获取到当前端点的信息,但是需要先开启当前端点对外功能,或者设置当前端点为默认开发的端点。
总结
端点的指标可以自定义,但是每种不同的指标根据其功能不同,自定义方式不同 info端点通过配置和编程的方式都可以添加端点指标 health端点通过编程的方式添加端点指标,需要注意要为对应指标添加启动状态的逻辑设定 metrics指标通过在业务中添加监控操作设置指标 可以自定义端点添加更多的指标