1、Actuator依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.hateoas</groupId>
<artifactId>spring-hateoas</artifactId>
<version>0.24.0.RELEASE</version>
</dependency>
代码中引入了spring-boot-starter-actuator,它是Spring Boot实施监控所必须的包。此外还引入了spring-hateoas。HATEOAS(The Hypermedia As The Engine Of Application Statue)是REST架构的主要约束。“hepermedia”表示任何包含指向图片、电影、文字等资源的链接,Web是超媒体的经典例子。HATEOAS背后的思想其实非常简单,就是响应中包含指向其它资源的链接。客户端可以利用这些链接和服务器交互。
有关HATEOAS的文章: Spring REST实践之HATEOAS
下面我们先来了解下Actuator提供了哪些端点来监控Spring Boot的运行状况。
2、Actuator内置的端点
id |
desc |
Sensitive |
auditevents |
显示当前应用程序的审计事件信息 |
Yes |
beans |
显示应用Spring Beans的完整列表 |
Yes |
caches |
显示可用缓存信息 |
Yes |
conditions |
显示自动装配类的状态及及应用信息 |
Yes |
configprops |
显示所有 @ConfigurationProperties 列表 |
Yes |
env |
显示 ConfigurableEnvironment 中的属性 |
Yes |
flyway |
显示 Flyway 数据库迁移信息 |
Yes |
health |
显示应用的健康信息(未认证只显示status ,认证显示全部信息详情) |
No |
info |
显示任意的应用信息(在资源文件写info.xxx即可) |
No |
liquibase |
展示Liquibase 数据库迁移 |
Yes |
metrics |
展示当前应用的 metrics 信息 |
Yes |
mappings |
显示所有 @RequestMapping 路径集列表 |
Yes |
scheduledtasks |
显示应用程序中的计划任务 |
Yes |
sessions |
允许从Spring会话支持的会话存储中检索和删除用户会话。 |
Yes |
shutdown |
允许应用以优雅的方式关闭(默认情况下不启用) |
Yes |
threaddump |
执行一个线程dump |
Yes |
httptrace |
显示HTTP跟踪信息(默认显示最后100个HTTP请求 - 响应交换) |
Yes |
Spring Boot中对这些端点提供了多种监控方式,包括HTTP和JMX等。下面,我们先来看看使用HTTP方式监控。
3、HTTP监控
注意:使用HTTP方式监控需要先导入spring-boot-starter-web包。
在导入好所需的依赖后,启动项目,访问http://localhost:8080/actuator/health,就可以看到当前应用的健康状态。
在默认情况下,Spring Boot的端点前缀是"/actuator"。我们也可以通过更改配置项来修改它为“/manage”:
management.endpoints.web.base-path=/manage
值得注意的是Spring Boot2.x
中,默认只开放了info、health
两个端点(因为端点往往会显示一些项目的敏感信息),剩余的需要自己通过配置management.endpoints.web.exposure.include
属性来加载所需端点(反之用exclude来排除不需要的端点
)。如果想单独操作某个端点可以使用management.endpoint.端点.enabled
属性进行启用或禁用。
比如说,访问除了env端点以外的所有端点,那么可以在application.properties文件中做如下配置:
# 暴露所有端点
management.endpoints.web.exposure.include=*
# 不暴露env端点
management.endpoints.web.exposure.exclude=env
但是通过这种方式访问端点是相当危险的,毕竟这些信息的权限应该属于系统的开发者和管理员,而不是每一个人。因此,我们应该通过Spring Security配置用户和角色,来解决这些敏感信息的访问权限问题。(需加入spring-boot-starter-security依赖)
package com.scb.actuatordemo.config;
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// 注册两个用户
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 密码加密器
PasswordEncoder passwordEncoder=new BCryptPasswordEncoder();
auth.inMemoryAuthentication().passwordEncoder(passwordEncoder)
.withUser("admin").password(passwordEncoder.encode("admin")).roles("USER","ADMIN").and()
.withUser("user").password(passwordEncoder.encode("user")).roles("USER");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
String[] endPoints={"auditevents","beans","conditions","configprops","env","flyway","httptrace",
"loggers","liquibase","metrics","mappings","scheduledtasks","sessions","shutdown","threaddump"};
http.requestMatcher(EndpointRequest.to(endPoints))
.authorizeRequests().anyRequest().hasRole("ADMIN")
.and()
// 后续shutdown端点的权限配置
.antMatcher("/close").authorizeRequests().anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
}
configure(HttpSecurity http)方法,首先定义了一个端点的数组,用来保存敏感信息的端点,而这些端点需要验证通过后才能访问。这里使用requestMatcher方法进行匹配请求,而EndpointRequest.to(...)方法是指定对应的端点,后续的hasRole是限定角色访问权限。这样对应的敏感信息除health和info之外,都只有拥有角色为“ROLE_ADMIN”的用户才能访问。
仍需将所有端点暴露出来:management.endpoints.web.exposure.include=*
shutdown端点
在所有端点中,shutdown端点是最特殊的一个。在默认情况下,Actuator并不会给开发者启动这个端点,因为请求它就会关闭服务器。如果想启用该端点需要在application.properties文件中做如下配置:
management.endpoint.shutdown.enabled=true
配置好了之后,shutdown端点就变为可用了。但它是一个POST请求,也就是无法通过浏览器地址栏进行访问。下面,我们通过jsp页面进行模拟:
/webapp/WEB-INF/jsp/close.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script src="./../js/jquery-3.3.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$("#submit").click(function () {
$.post({
url: "./actuator/shutdown",
success:function (result) {
if (result!=null || result.message!=null){
alert(result.message);
return;
}
alert("close failure")
}
})
})
})
</script>
<title>Test shutdown endpoint</title>
</head>
<body>
<input id="submit" type="button" value="shutdown">
</body>
</html>
ActuatorController.class
package com.scb.actuatordemo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
@RestController
public class ActuatorController {
@GetMapping("/close")
public ModelAndView close(ModelAndView mv){
mv.setViewName("close");
return mv;
}
}
接下来访问http://localhost:8080/close,点击按钮“shutdown”后,服务就会关闭了。
4、JMX监控
对于Spring Boot,还可以通过Java管理扩展(Java Management Extensions,JMX)来让开发人员监控JVM的状况。
进入JAVA_HOME目录下的bin目录,找到jconsole.exe文件,在运行Spring Boot的项目时在运行它。可以看到如下所示的对话框:
选中Spring Boot项目,点击“Connect”就能够连接到Spring Boot运行的环境,从而监控JVM的运行状态。
5、自定义端点
除了使用Actuator默认给予的端点外,我们还可以自定义端点来满足自定义监控的要求。在Actuator中加入端点只需要加入注解@Endpoint即可,这个注解会同时提供JMX监控和Web监控。如果只想提供JMX监控,可以使用@JmxEndpoint;如果只想提供Web监控,可以使用注解@WebEndpoint。
package com.scb.actuatordemo.endpoint;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@Component
@Endpoint(id = "endpointTest",enableByDefault = true)
public class MyEndPoint {
@ReadOperation
public Map<String,String> test(){
Map<String,String> result=new HashMap<>();
result.put("author","Ni");
result.put("time","2019/02/25");
return result;
}
}
-
@ReadOperation:
GET请求,响应状态为 200 如果没有返回值响应 404(资源未找到)
-
@WriteOperation:
POST请求,响应状态为 200 如果没有返回值响应 204(无响应内容)
-
@DeleteOperation:
DELETE请求,响应状态为 200 如果没有返回值响应 204(无响应内容)
运行项目,访问http://localhost:8080/actuator/endpointTest
6、健康指标项
Actuator默认提供的监控指标项
名称 |
描述 |
CassandraHealthIndicator |
检查Cassandra 数据库是否启动。 |
DiskSpaceHealthIndicator |
检查磁盘空间不足。 |
DataSourceHealthIndicator |
检查是否可以获得连接DataSource 。 |
ElasticsearchHealthIndicator |
检查Elasticsearch 集群是否启动。 |
InfluxDbHealthIndicator |
检查InfluxDB 服务器是否启动。 |
JmsHealthIndicator |
检查JMS 代理是否启动。 |
MailHealthIndicator |
检查邮件服务器是否启动。 |
MongoHealthIndicator |
检查Mongo 数据库是否启动。 |
Neo4jHealthIndicator |
检查Neo4j 服务器是否启动。 |
RabbitHealthIndicator |
检查Rabbit 服务器是否启动。 |
RedisHealthIndicator |
检查Redis 服务器是否启动。 |
SolrHealthIndicator |
检查Solr 服务器是否已启动。 |
上表所示的健康指标是Actuator根据开发者配置的项目进行自动创建的,但是他们在默认情况下,是不会进行展示的。要展示这些健康项的时候,需要进行如下配置:
# never:从不展示健康项
# when-authorized:签名认证之后展示
# always:每次都展示
management.endpoint.health.show-details=when_authorized
此时,在访问http://localhost:8080/actuator/health
上图展示了所有开发者配置内容的健康指标。但是如果不想开启所有指标,可以根据情况关闭对应的健康指标项监控。例如,关闭数据库的健康指标可以进行如下配置:
management.health.db.enabled=false
对于健康指标,除了关注如何查看外,还需要关注它们的严重级别,如默认的配置项所示:
management.health.status.order=DOWN, OUT_OF_SERVICE, UP, UNKNOWN
这里是问题级别从重到轻的排序,它们使用逗号分割,其配置项含义为:
- DOWN:下线
- OUT_OF_SERVICE:不在提供服务
- UP:启动
- UNKNOWN:未知
下面我们看看如何开发自定义的健康指标:
package com.scb.actuatordemo.endpoint;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.stereotype.Component;
import java.net.InetAddress;
/**
* 监测服务器是否可以访问万维网
*/
@Component
public class WwwHealthIndicator extends AbstractHealthIndicator {
private final static String BAIDU_HOST="www.baidu.com";
private final static int TIME_OUT=3000;
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
boolean status=ping();
if(status){
builder.withDetail("message","当前服务器可以访问万维网。").up();
}else {
builder.withDetail("message","当前服务器无法访问万维网。").outOfService();
}
}
// 监测是否能够访问百度,用以判断能否访问万维网
private boolean ping() throws Exception{
try{
return InetAddress.getByName(BAIDU_HOST).isReachable(TIME_OUT);
}catch (Exception ex){
return false;
}
}
}
再次访问http://localhost:8080/actuator/health
我这里使用的是公司电脑,需要代理,所以无法访问。