什么是健康检查?
健康检查是一种用于检查应用程序或其依赖服务是否正常运行的机制。通过定期检查应用程序,可以及时发现问题并采取措施解决它们,从而提高应用程序的可靠性和稳定性。Spring Boot 提供了一个内置的健康检查机制,可以方便地检查应用程序的状态。
健康检查可以包括以下内容:
检查应用程序是否可以响应请求。
检查应用程序所依赖的服务是否可以正常访问。
检查应用程序的资源使用情况,如内存和 CPU 使用情况。
检查应用程序的配置是否正确。
Spring Boot 的健康检查
Spring Boot 提供了一个名为 Actuator 的插件,它包括了许多有用的功能,包括健康检查。Actuator 可以通过 HTTP 端点公开应用程序的状态和管理信息,包括健康检查信息。
在 Spring Boot 应用程序中,只需要简单地添加 Actuator 依赖即可启用健康检查功能。在 Maven 项目中,可以在 pom.xml 文件中添加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
添加依赖后,可以通过 HTTP 端点访问健康检查信息。可以看到,默认暴露了两个endpoint,其实就是 health 和 info
打开地址:http://localhost:8080/actuator/info 或者 http://localhost:8080/actuator/health
Spring Boot Actuator 提供了许多有用的健康检查指标和监控工具,包括:
/actuator/health:显示应用程序的健康检查信息。
/actuator/metrics:显示应用程序的度量信息,如请求速率、响应时间等。
/actuator/loggers:显示应用程序的日志配置信息。
/actuator/httptrace:显示应用程序的 HTTP 跟踪信息。
/actuator/threaddump:显示应用程序的线程转储信息。
我们可以通过配置去开启这些端点
management:
endpoints:
web:
base-path: / # root path
exposure:
include: "*" # include all endpoint
endpoint:
gateway:
enabled: true #开启gateway网关的端点
除了以上这些功能外,Spring Boot Actuator 还提供了许多其他有用的监控工具,可以帮助我们监视应用程序的状态和性能。
言归正传,那如何实现对这些请求进行认证呢?
springMvc架构代码实现如下:
@WebFilter(filterName = "ActuatorAuthenticationFilter ", urlPatterns = {"/actuator/**"})
public class ActuatorAuthenticationFilter implement Filter {
private static final Logger LOGGER = LoggerFactory.getLogger(ActuatorAuthenticationFilter.class);
private static final String AUTHORIZATION = "Authorization";
private static final String BASIC = "Basic ";
//配置的actuator账号
private String actuatorName = "name";
//配置的actuator密码
private String actuatorPassword = "password";
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
if (!(servletRequest instanceof HttpServletRequest) || !(servletResponse instanceof HttpServletResponse)) {
filterChain.dofilter(servletRequest, servletResponse);
return;
}
HttpServletRequest request = (HttpServletRequest) servletRequest;
String path = request.getRequestURI().substring(request.getContextPath().length()).replaceAll("[/]+$", "");
String headerAuthorization = request.getHeader(AUTHORIZATION);
String encodeString = BASIC + Base64.getEncoder().encodeToString((actuatorName + ":" + actuatorPassword).getBytes(StandardCharsets.UTF_8));
if (headerAuthorization != null && headerAuthorization.equals(encodeString)) {
filterChain.dofilter(servletRequest, servletResponse);
return;
}
LOGGER.info("被拦截路径[{}]未获得访问权限", path);
HttpServletResponse res = (HttpServletResponse) servletResponse;
res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
gateway代码如下:
@Component
public class ActuatorAuthenticationFilter implement WebFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(ActuatorAuthenticationFilter.class);
private static final PathPattern FILTER_PATH_PATTERN = new PathPatternParser().parse("/actuator/**");
private static final String AUTHORIZATION = "Authorization";
private static final String BASIC = "Basic ";
private static final String ALREADY_FILTERED_SUFFIX = ".FILTERED";
//配置的actuator账号
private String actuatorName = "name";
//配置的actuator密码
private String actuatorPassword = "password";
@Override
public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
//处理同一请求走两次,只在第一次进行自定义逻辑处理
if(!hasFilter(serverWebExchange)) {
//设置一个标记
setFilteredAttributeName(serverWebExchange);
PathContainer pathContainer = serverWebExchange.getRequest().getPath().pathWithinApplication();
if(FILTER_PATH_PATTERN.matches(pathContainer)){
String uri = pathContainer.value();
LOGGER.info("WebFilter拦截路径:{}", uri);
String headerAuthorization = serverWebExchange.getRequest().getHeaders().getFirst(AUTHORIZATION);
if(StringUtils.isBlank(headerAuthorization)){
return unAuthorizedResponse(uri, serverWebExchange);
}
String encodeString = BASIC + Base64.getEncoder().encodeToString((actuatorName + ":" + actuatorPassword).getBytes(StandardCharsets.UTF_8));
if(!encodeString.equals(headerAuthorization)) {
return unAuthorizedResponse(uri, serverWebExchange);
}
}
}
return webFilterChain.filter(serverWebExchange);
}
private boolean hasFilter(ServerWebExchange serverWebExchange) {
return Boolean.TRUE.equals(serverWebExchange.getAttributes().get(getFilteredAttributeName()));
}
private boolean setFilteredAttributeName(ServerWebExchange serverWebExchange) {
serverWebExchange.getAttributes().put(getFilteredAttributeName(), Boolean.TRUE);
}
private String getFilteredAttributeName(){
return getClass().getName() + ALREADY_FILTERED_SUFFIX;
}
private Mono<Void> unAuthorizedResponse(String uri, ServerWebExchange serverWebExchange){
LOGGER.info("被拦截路径[{}]未获得访问权限", uri);
ServerHttpResponse response = serverWebExchange.getResponse();
response.getHeader().add(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8");
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return Mono.empty();
}
}