SpringBoot提供了/actuator/health健康检查的接口,接着我们从前往后看这个请求是怎么被SpringBoot处理的,当然入口还是DispatcherServlet。

SpringCloud 健康指标 spring 健康检查接口_SpringCloud 健康指标


第一个迭代的是

org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping

它继承了AbstractWebMvcEndpointHandlerMapping,而这个mapping又继承RequestMappingInfoHandlerMapping,接着这个mapping又继承了AbstractHandlerMethodMapping。此时调的这个getHandlerInternal方法就在RequestMappingInfoHandlerMapping中,但是很不幸,它又调了super的也就是AbstractHandlerMethodMapping#getHandlerInternal方法,如下图所示。看起来听绕的,不过都是一层层的继承关系,只不过层次稍微多了点。

SpringCloud 健康指标 spring 健康检查接口_spring_02


看下这个方法:

其中先找到了请求的路径lookupPath即/actuator/health.然后根据这个path在mappingRegistry里找映射的handlerMethod。

SpringCloud 健康指标 spring 健康检查接口_spring_03


这里边很明显的有一个mappingRegistry映射注册表,里边其实就是url和HandlerMapping的映射关系集合。那么,就可以从中找到/actuator/health的HandlerMapping。

SpringCloud 健康指标 spring 健康检查接口_健康检查_04


SpringCloud 健康指标 spring 健康检查接口_Spring_05


也就是org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$OperationHandler#handle(HttpServletRequest, Map)。

然后这个方法就返回了,跳出了org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal之后,又回到了org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler方法,handler非空且非String,那么就看看这个handler有没有定义org.springframework.web.servlet.HandlerInterceptor拦截器?如果定义的话加入到org.springframework.web.servlet.HandlerExecutionChain中。

SpringCloud 健康指标 spring 健康检查接口_SpringCloud 健康指标_06


这个HandlerExecutionChain才是我们要返回的对象,看下它的构造方法,不用多想HandlerMethod和HandlerInterceptor都在其中。

/**
	 * Create a new HandlerExecutionChain.
	 * @param handler the handler object to execute
	 */
	public HandlerExecutionChain(Object handler) {
		this(handler, (HandlerInterceptor[]) null);
	}

很走运,第一个mapping就找到了我们的结果。

SpringCloud 健康指标 spring 健康检查接口_Spring_07


DispatcherServlet拿到了HandlerExecutionChain还不够,还得看内置的Adapter列表中哪一个是支持这个HandlerMethod的。

// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

内置的有四个HandlerAdapter,很走运,第一个就合适RequestMappingHandlerAdapter

SpringCloud 健康指标 spring 健康检查接口_SpringCloud 健康指标_08


SpringCloud 健康指标 spring 健康检查接口_spring_09


在执行具体方法前后会执行HandlerInterceptor,但是我们没有定义HandlerInterceptor,这里不考虑,直接跳过。

SpringCloud 健康指标 spring 健康检查接口_SpringBoot_10


然后执行org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapterhandle方法。

而RequestMappingHandlerAdapter实现了它。最后执行到RequestMappingHandlerAdapter中的invokeHandlerMethod方法,这个方法哪里通过invocableMethod.invokeAndHandle(webRequest, mavContainer);执行具体调用。一直走下去到org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest方法。如下:

SpringCloud 健康指标 spring 健康检查接口_SpringCloud 健康指标_11


SpringCloud 健康指标 spring 健康检查接口_Spring_12

  • getBridgedMethod():就是HandlerMethod的方法。org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$OperationHandler#handle(HttpServletRequest, Map)
  • getBean():就是org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$OperationHandler

来看看在这个

org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.OperationHandler

这个OperationHandler#handle方法中还继续调用了org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.ServletWebOperationAdapter#handle的方法。

SpringCloud 健康指标 spring 健康检查接口_Spring_13


SpringCloud 健康指标 spring 健康检查接口_spring_14


可以从headers中看到user-agent是来自于consul的健康检查:

host:“172.30.200.34:8080”,
user-agent:“Consul Health Check”,
accept:“text/plain, text/*, /”,
accept-encoding:“gzip”,
connection:“close”

最后执行的是org.springframework.boot.actuate.endpoint.web.WebOperation接口的实现类org.springframework.boot.actuate.endpoint.web.annotation.DiscoveredWebOperation,这个类继承了org.springframework.boot.actuate.endpoint.annotation.AbstractDiscoveredOperation。如下:

SpringCloud 健康指标 spring 健康检查接口_健康检查_15


上图的invoker其实是org.springframework.boot.actuate.endpoint.invoke.reflect.ReflectiveOperationInvoker,看下图就是利用反射机制执行这个invoker的target对象(HealthEndpointWebExtension对象)的health方法。

SpringCloud 健康指标 spring 健康检查接口_spring_16


SpringCloud 健康指标 spring 健康检查接口_Spring_17


沿着逻辑一直往下走,会走到org.springframework.boot.actuate.health.HealthIndicatorgetHealth方法。

但这个方法是个抽象方法,需要子类去覆盖。在子类中我么可以去做一些操作,比如判断服务如果没有注册到consul,则将服务重新注册到consul上。因为实际生产中可能会出现consul集群宕机后重启的情况,此时就需要在感知到/actuator/health健康检查到来的时候重新注册上去。

SpringCloud 健康指标 spring 健康检查接口_Spring_18


如果重新注册成功,我们就可以将服务标识为健康:

Health health = new Health.Builder().up().build();

并返回。

SpringCloud 健康指标 spring 健康检查接口_SpringBoot_19


在SpringBoot中有一个实现,默认情况下Conusl会定时(10s)发送请求/actuator/health到这个类中,直接返回的up。当然我们也可以自定义。

/*
 * Copyright 2012-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.actuate.health;

/**
 * Default implementation of {@link HealthIndicator} that returns {@link Status#UP}.
 *
 * @author Dave Syer
 * @author Christian Dupuis
 * @since 2.2.0
 * @see Status#UP
 */
public class PingHealthIndicator extends AbstractHealthIndicator {

	@Override
	protected void doHealthCheck(Health.Builder builder) throws Exception {
		builder.up();
	}

}