Spring Boot的Actuator模块在生产环境中为应用程序提供了关键的监控和度量功能。这些功能主要通过REST端点、远程shell和JMX等技术实现,其中REST端点是最为人们所熟知的,并且提供了全面的功能集。通过Actuator,开发人员可以轻松地管理和监控运行中的Spring Boot应用程序,确保其稳定性和性能。
生产环境监控与度量
Actuator Web端点
Actuator是Spring Boot的一个子项目,它为Spring Boot应用提供了管理和监控功能。通过Actuator的Web端点,开发人员可以轻松地查看和管理运行中的Spring Boot应用程序。
2. 调整Actuator
为了更好地适应不同的应用场景和需求,Actuator提供了许多可配置的参数。开发人员可以根据实际需要对这些参数进行调整,如修改端点的安全性设置、调整监控数据的收集频率等。
3. 通过shell连入运行中的应用程序
对于正在运行的应用程序,可以通过shell进行连接和交互。这使得开发人员可以执行特定的命令或脚本,以获取应用程序的实时状态或进行故障排查。
4. 保护Actuator
为了确保Actuator的安全性,开发人员应采取必要的保护措施。例如,限制对Actuator端点的访问权限、使用HTTPS进行通信等。这样可以防止未经授权的访问和潜在的安全风险。
Spring Boot Actuator的核心优势在于其提供的众多Web端点,这些端点为开发人员提供了应用程序运行时的内部状况的详细信息。
通过Actuator,你可以深入了解Spring应用程序上下文中的Bean如何组装在一起,掌握应用程序的环境属性,以及获取运行时的度量信息快照。
这些功能对于监控、调试和优化应用程序的性能至关重要,端点可以分为三大类:配置端点、度量端点和其他端点。让我们分别了解一下这些端点,从提供应用程序配置信息的端点看起。
HTTP方法 | 路径 | 描述 |
---|---|---|
GET | /autoconfig | 提供了丰富的监控和度量功能,还为用户提供了一份详细的自动配置报告。这份报告记录了哪些自动配置条件已通过,哪些未通过 |
GET | /configprops | 配置属性,这些配置属性可以设置默认值,并在运行时根据需要进行调整 |
GET | /beans | 描述应用程序上下文里全部的Bean,以及它们的关系 |
GET | /dump | 获取线程活动快照的功能 |
GET | /env | 获取全部环境属性 |
GET | /env/{name} | 根据名称获取特定的环境属性值 |
GET | /health | 报告应用程序的健康指标,这些值由HealthIndicator的实现类提供 |
GET | /info | 获取应用程序的定制信息,这些信息由info打头的属性提供 |
GET | /mappings | 描述全部的URI路径,以及它们和控制器(包含Actuator端点)的映射关系 |
GET | /metrics | 报告各种应用程序度量信息,比如内存用量和HTTP请求计数 |
GET | /metrics/{name} | 报告指定名称的应用程序度量值 |
GET | /shutdown | 关闭应用程序,要求endpoints.shutdown.enabled设置为true |
GET | /trace | 提供基本的HTTP请求跟踪信息(时间戳、HTTP头等) |
要启用Actuator的端点,只需在项目中引入Actuator的起步依赖即可。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
compile 'org.springframework.boot:spring-boot-starter-actuator'
无论Actuator是如何添加的,在应用程序运行时自动配置都会生效,Actuator会开启。
在Spring开发中,组件扫描和自动织入是常见的功能,但如何清晰地看到应用程序中的组件是如何装配起来的,却一直是困扰开发人员的问题。
Spring Boot Actuator的出现,为解决这一问题提供了新的途径。通过Actuator提供的Web端点,开发人员可以直观地查看组件的装配过程,深入了解Bean在Spring应用程序上下文中的关系和行为。这不仅有助于解决配置和装配过程中的问题,还能为性能优化和故障排查提供关键的信息。
了解应用程序中Spring上下文的情况,最重要的端点就是/beans。它会返回一个JSON数据,描述上下文里每个Bean的情况,包括其Java类型以及注入的其他Bean。
向/beans(在本地运行时是http://ip:port/beans)发起GET请求后。
{
"beans": [
{
"bean": "application",
"dependencies": [],
"resource": "null",
"scope": "singleton",
"type": "test.Application$$EnhancerBySpringCGLIB$$f363c202"
}
...
],
"context": "application",
"parent": null
}
]
所有的Bean模型下面都有五个属性:
Spring Boot Actuator的beans端点提供了一份详尽的报告,让你清楚了解Spring应用程序上下文中都包含了哪些Bean。这份报告不仅列出了Bean的名称或ID,还提供了关于Bean的其他关键信息。
与此同时,autoconfig端点则深入解析了Bean的来源。它解释了为什么某个特定的Bean会出现,或者为什么某个Bean没有出现。这对于理解自动配置的逻辑和决策过程非常有价值,有助于解决配置和依赖注入过程中的问题。
{
"positiveMatches": {
...
"DataSourceAutoConfiguration.JdbcTemplateConfiguration #
jdbcTemplate ": [ {
"condition": "OnBeanCondition",
"message": "@ConditionalOnMissingBean (types:
org.springframework.jdbc.core.JdbcOperations;
SearchStrategy: all) found no beans "
}
],
...
},
"negativeMatches": {
"ActiveMQAutoConfiguration": [{
"condition": "OnClassCondition",
"message": "required @ConditionalOnClass classes not found:
javax.jms.ConnectionFactory,
org.apache.activemq
.ActiveMQConnectionFactory "
}],
...
}
}
positiveMatches
部分会发现一个决定Spring Boot是否自动配置JdbcTemplate
Bean的条件。该条件匹配项被称为DataSourceAutoConfiguration.JdbcTemplateConfiguration#jdbcTemplate
,这表明满足特定条件的自动配置类已被应用。
条件类型为OnBeanCondition
,这意味着是否满足该条件取决于是否存在特定的Bean。
在这个例子中,message
属性明确指出,该条件正在检查系统中是否存在JdbcOperations
类型的Bean(JdbcTemplate
实现了这个接口)。如果系统中未配置此类型的Bean,那么该条件将被满足,并将创建一个新的JdbcTemplate
Bean。
negativeMatches
部分有一个条件用于决定是否应当配置ActiveMQ。这是基于OnClassCondition
的条件,其检查目标类路径(Classpath)是否含有ActiveMQConnectionFactory
类。由于类路径中不存在此类,因此条件不成立,Spring Boot将不会自动配置ActiveMQ。
通过访问 /env
端点,你能够查看应用程序中所有可用的环境属性清单,不论这些属性是否被实际使用。这包含从多个来源汇总而来的信息,如环境变量、JVM 系统属性、命令行参数,以及来自 application.properties
或 application.yml
文件的配置属性。这项特性为理解和调试应用程序提供了一个全面的视角。
基本上,任何能给Spring Boot应用程序提供属性的属性源都会列在/env的结果里,同时会显示具体的属性。
属性的来源构成了一个多层次、多维度的体系,涉及从应用程序配置文件(例如application.yml)的基础参数设定到Spring Profile的精细化策略定制。进一步扩展,属性可能源自Web应用的Servlet上下文初始化参数,亦或是更广泛的系统环境变量,以及与Java虚拟机(JVM)相关的系统属性。
{
"applicationConfig: [classpath:/application.yml]": {
"amazon.associate_id": "habuma-20",
"error.whitelabel.enabled": false,
"logging.level.root": "INFO"
},
"profiles": [],
"servletContextInitParams": {},
"systemEnvironment": {
"BOOK_HOME": "/Users/habuma/Projects/BookProjects/walls6",
"GRADLE_HOME": "/Users/habuma/.sdkman/gradle/current",
"GRAILS_HOME": "/Users/habuma/.sdkman/grails/current",
"GROOVY_HOME": "/Users/habuma/.sdkman/groovy/current",
...
},
"systemProperties": {
"PID": "682",
"file.encoding": "UTF-8",
"file.encoding.pkg": "sun.io",
"file.separator": "/",
...
}
}
属性在应用程序中承担着配置和敏感数据传递的重要角色,其中包括但不限于数据库凭据或API访问密钥。出于安全考虑,确保这些敏感信息不被无意间泄露至系统外部至关重要。为此,Actuator的/env端点在展示环境属性时采用了一项隐蔽措施:任何包含关键词如password
、secret
、key
,或其变量名的最后一部分含有这些关键词的属性值,都将在/env端点中以星号(*
)显示,以此来马赛克敏感内容。例如,如果存在一个属性名为database.password
,在/env端点中,其值将不会直接显示,而是如下所示:
"database.password": "******"
这种措施能有效地减少敏感信息通过系统监控工具被不当访问或泄漏的风险。
/env 端点不仅提供了查看环境属性的能力,还可以用来检索特定的属性值。例如,想要获取阅读列表应用程序中 amazon.associate_id 的值,可以简单地向 /env/amazon.associate_id 发起请求,这将返回 habuma-20。
提到环境属性的使用可以通过@ConfigurationProperties 注解实现极高的便捷性。通过此注解,相关属性会自动填充至标有 @ConfigurationProperties 的 Bean 的相应字段中。 /configprops 端点则提供了一个详细报告,展示了这些属性是如何设置的。
"management.endpoints.web-org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties": {
"prefix": "management.endpoints.web",
"properties": {
"pathMapping": {},
"exposure": {
"include": [
"*"
],
"exclude": []
},
"basePath": "/actuator",
"discovery": {
"enabled": true
}
},
"inputs": {
"pathMapping": {},
"exposure": {
"include": [
{
"value": "*",
"origin": "class path resource [application.properties] - 4:43"
}
],
"exclude": []
},
"basePath": {},
"discovery": {
"enabled": {}
}
}
}
在小型应用程序中,跟踪控制器映射到的端点相对简单。但随着应用程序的扩展,Web界面的控制器和请求处理方法数量可能会快速增长,此时若能有一个清晰的列表显示应用程序公开的所有端点,则大大有助于管理和维护。为此,/mappings 端点提供了一个实用的解决方案,它生成了一个详细的端点映射列表,使开发者和管理员能够一目了然地掌握整个应用程序的接口结构。
{
"contexts": {
"application": {
"mappings": {
"dispatcherServlets": {
"dispatcherServlet": [
{
"handler": "Actuator web endpoint 'scheduledtasks'",
"predicate": "{GET [/actuator/scheduledtasks], produces [application/vnd.spring-boot.actuator.v3+json || application/vnd.spring-boot.actuator.v2+json || application/json]}",
"details": {
"handlerMethod": {
"className": "org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.OperationHandler",
"name": "handle",
"descriptor": "(Ljakarta/servlet/http/HttpServletRequest;Ljava/util/Map;)Ljava/lang/Object;"
},
"requestMappingConditions": {
"consumes": [],
"headers": [],
"methods": [
"GET"
],
"params": [],
"patterns": [
"/actuator/scheduledtasks"
],
"produces": [
{
"mediaType": "application/vnd.spring-boot.actuator.v3+json",
"negated": false
},
{
"mediaType": "application/vnd.spring-boot.actuator.v2+json",
"negated": false
},
{
"mediaType": "application/json",
"negated": false
}
]
}
}
},
"servletFilters": [
{
"servletNameMappings": [],
"urlPatternMappings": [
"/*"
],
"name": "requestContextFilter",
"className": "org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter"
},
{
"servletNameMappings": [],
"urlPatternMappings": [
"/*"
],
"name": "webMvcObservationFilter",
"className": "org.springframework.web.filter.ServerHttpObservationFilter"
},
{
"servletNameMappings": [],
"urlPatternMappings": [
"/*"
],
"name": "Tomcat WebSocket (JSR356) Filter",
"className": "org.apache.tomcat.websocket.server.WsFilter"
},
{
"servletNameMappings": [],
"urlPatternMappings": [
"/*"
],
"name": "characterEncodingFilter",
"className": "org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter"
},
{
"servletNameMappings": [],
"urlPatternMappings": [
"/*"
],
"name": "formContentFilter",
"className": "org.springframework.boot.web.servlet.filter.OrderedFormContentFilter"
}
],
"servlets": [
{
"mappings": [
"/"
],
"name": "dispatcherServlet",
"className": "org.springframework.web.servlet.DispatcherServlet"
}
]
}
}
}
}
在上面的内容中,我们可以观察到众多的端点映射。每一个映射实体都以字符串形式作为键,这些字符串反映了Spring MVC中@RequestMapping
注解所定义的属性。这些映射字符串为我们提供了透彻的视角,即使不查阅源代码,也能清楚地理解控制器的映射机制。映射实体的值包含两个关键属性:bean
和method
。
Actuator的配置端点能很方便地让你了解应用程序是如何配置的。能看到应用程序在运行时究竟发生了什么,这很有趣、很实用。度量端点能展示应用程序运行时内部状况的快照。
为了有效评估应用程序的健康状况,进行实时度量数据的快照至关重要。Actuator设计了一系列端点,使得您可以在应用程序运行期间进行便捷地检查。让我们深入探究这些端点的功能,特别是从 /metrics
端点开始。
对于正在运行的应用程序,我们可以获取许多有趣且实用的信息。例如,对应用程序的内存使用情况(包括可用内存或剩余内存)的了解,可以帮助我们决策合适的JVM内存分配量。对于Web应用程序,不必翻阅繁杂的Web服务器日志,我们就能通过监测请求失败或响应时间过长的情况,大致推断内存使用的状态。
在应用程序运行期间,众多的计数器和度量工具正实时记录着关键的性能指标。/metrics
端点就提供了这样一个方便的窗口,让我们能够随时抓取这些关键数据的即时快照。
{
mem: 198144,
mem.free: 144029,
processors: 8,
uptime: 1887794,
instance.uptime: 1871237,
systemload.average: 1.33251953125,
heap.committed: 198144,
heap.init: 131072,
heap.used: 54114,
heap: 1864192,
threads.peak: 21,
threads.daemon: 19,
threads: 21,
classes: 9749,
classes.loaded: 9749,
classes.unloaded: 0,
gc.ps_scavenge.count: 22,
gc.ps_scavenge.time: 122,
gc.ps_marksweep.count: 2,
gc.ps_marksweep.time: 156,
httpsessions.max: -1,
httpsessions.active: 1,
datasource.primary.active: 0,
datasource.primary.usage: 0,
counter.status.200.beans: 1,
counter.status.200.env: 1,
counter.status.200.login: 3,
counter.status.200.metrics: 2,
counter.status.200.root: 6,
counter.status.200.star-star: 9,
counter.status.302.login: 3,
counter.status.302.logout: 1,
counter.status.302.root: 5,
gauge.response.beans: 169,
gauge.response.env: 165,
gauge.response.login: 3,
gauge.response.logout: 0,
gauge.response.metrics: 2,
gauge.response.root: 11,
gauge.response.star-star: 2
}
/metrics端点提供了很多信息,在此我们进行了分类,如下表所示:
分 类 | 前 缀 | 报告内容 |
---|---|---|
垃圾收集器 | gc.* | 已经发生过的垃圾收集次数,以及垃圾收集所耗费的时间,适用于标记-清理垃圾收集器和并行垃圾收集器(数据源自 java.lang.management.GarbageCollectorMXBean ) |
内存 | mem.* | 分配给应用程序的内存数量和空闲的内存数量(数据源自java.lang.Runtime ) |
堆 | heap.* | 当前内存用量(数据源自java.lang.management.MemoryUsage ) |
类加载器 | classes.* | JVM类加载器加载与卸载的类的数量(数据源自java.lang.management.ClassLoadingMXBean ) |
系统 | processors 、uptime 、instance.uptime 、systemload.average | 系统信息,例如处理器数量(数据源自java.lang.Runtime )、运行时间(数据源自java.lang.management.RuntimeMXBean )、平均负载(数据源自java.lang.management.OperatingSystemMXBean ) |
线程池 | threads.* | 线程、守护线程的数量,以及JVM启动后的线程数量峰值(数据源自java.lang.management.ThreadMXBean ) |
数据源 | datasource.* | 数据源连接的数量(源自数据源的元数据,仅当Spring应用程序上下文里存在DataSource Bean的时候才会有这个信息) |
Tomcat会话 | httpsessions.* | Tomcat的活跃会话数和最大会话数(数据源自嵌入式Tomcat的Bean,仅在使用嵌入式Tomcat服务器运行应用程序时才有这个信息) |
HTTP计数器 | counter.status.* 、gauge.response.* | 多种应用程序服务HTTP请求的度量值与计数器 |
HTTP的计数器和度量值需要做一点说明。counter.status后的值是HTTP状态码,随后是所请求的路径。举个例子,counter.status.200.metrics表明/metrics端点返回200(OK)状态码的次数。
需要特别注意的是,有几个特殊的路径值。root
路径指的是根目录,即 /
。**
(双星)代表了Spring框架认定的静态资源的路径,这些静态资源包括图片、JavaScript 文件和样式表等。此外,双星路径还涵盖了那些无法找到的资源。这解释了为什么我们常常会见到 counter.status.404.**
这样的路径,它记录了返回HTTP 404状态码(未找到)的请求次数。
除了上述的数据度量之外,你还可以注册自己的度量信息。
/metrics端点会返回所有的可用度量值,但你也可能只对某个值感兴趣。要获取单个值,请求时可以在URL后加上对应的键名。例如,要查看空闲内存大小,可以向/metrics/mem.free发一个GET请求:
$ curl localhost:8080/metrics/mem.free
1444231
尽管/metrics
端点为Web请求提供了一些基础的计数和计时信息,但这些数据往往不够详尽。在进行故障排查时,能够获得关于处理请求的更细节信息显得尤为重要。因此,/trace
端点的作用就凸显出来了。它不仅记录了每个Web请求的详细信息,如请求方法、请求路径、时间戳,还包括了请求与响应的头部信息。代码清单7-7展示了/trace
端点输出的样例,其中包含了对请求跟踪项的完整描述。
[
...
{
"timestamp": 1426378239775,
"info": {
"method": "GET",
"path": "/metrics",
"headers": {
"request": {
"accept": "*/*",
"host": "localhost:8080",
"user-agent": "curl/7.37.1"
},
"response": {
"X-Content-Type-Options": "nosniff",
"X-XSS-Protection": "1; mode=block",
"Cache-Control": "no-cache, no-store, max-age=0, must-revalidate",
"Pragma": "no-cache",
"Expires": "0",
"X-Frame-Options": "DENY",
"X-Application-Context": "application",
"Content-Type": "application/json;charset=UTF-8",
"Transfer-Encoding": "chunked",
"Date": "Sun, 15 Mar 2015 00:10:39 GMT",
"status": "200"
}
}
}
}
]
正如method和path属性所示,你可以看到这个跟踪项是一个针对/metrics的请求。timestamp属性(以及响应中的Date头)告诉了你请求的处理时间。headers属性的内容是请求和响应中所携带的头信息。
在处理微服务架构下运行在云平台上的应用程序时,我们有时会遇到某个服务实例出现问题,需要进行重启以确保服务的正常运行和稳定性。在这种情况下,使用Actuator的/shutdown
端点可以是一个高效的方法。具体而言,当我们确定一个服务实例需要关闭时,可以向该实例的/shutdown
端点发送一个HTTP POST请求。这个操作会安全地关闭服务实例,之后云服务提供商可以根据配置的策略来自动重启该服务。这样做的好处是可以最小化服务中断的时间,并确保服务的弹性和可靠性。
注意,这一做法应该谨慎使用,并确保你拥有适当的权限,并且这一策略符合你的应用程序部署和运维策略。此外,开启Actuator的
/shutdown
端点可能带来安全风险,因此请确保适当保护该端点,避免未授权的访问。
可以用命令行工具curl来关闭应用程序:
$ curl -X POST http://ip:port/shutdown
很显然,关闭运行中的应用程序是件危险的事情,因此这个端点默认是关闭的。如果没有显式地开启这个功能,那么POST请求的结果是这样的:
{"message":"This endpoint is disabled"}
要开启该端点,可以将endpoints.shutdown.enabled设置为true。举例来说,可以把如下内容加入application.yml,借此开启/shutdown端点:
endpoints:
shutdown:
enabled: true
打开/shutdown端点后,你要确保并非任何人都能关闭应用程序。这时应该保护/shutdown端点,只有经过授权的用户能关闭应用程序。