乐于分享
好东西不私藏

源码分析:关于SpringBoot的健康检查

源码分析:关于SpringBoot的健康检查

大家都知道SpringBoot提供了耳熟能详的/actuator/health

健康检查的接口,但你是否清楚这个请求是怎么被SpringBoot处理的?  

你没有猜错,跟其他web请求一样入口还是从DispatcherServlet说起!

这里对handlerMappings进行迭代,第一个迭代的是

org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping
它继承了AbstractWebMvcEndpointHandlerMapping,而这个抽象的mapping类又继承了另外一个看起来更为眼熟的RequestMappingInfoHandlerMapping接着这个”大众脸”的mapping又继承了另一个更为大众脸的抽象的AbstractHandlerMethodMapping。下图调的getHandlerInternal方法就前面提到RequestMappingInfoHandlerMapping中,但它又调了super的—也就是抽象类中的那一个AbstractHandlerMethodMapping的getHandlerInternal方法,如下图所示。看起来听绕的,但也不过都是一层层的继承关系,只不过层次稍微多了点。

看下这个方法,其中的流程是先找到了请求的路径lookupPath即/actuator/health.然后根据这个path在mappingRegistry里找映射的handlerMethod。

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

也就是

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有没有定义这个Spring的拦截器类org.springframework.web.servlet.HandlerInterceptor如果定义的话就加入到org.springframework.web.servlet.HandlerExecutionChain中。

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

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

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

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

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

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

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

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

- 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

可以从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

上图的invoker其实是:

org.springframework.boot.actuate.endpoint.invoke.reflect.ReflectiveOperationInvoker

看下图就是利用反射机制执行这个invoker的target对象(HealthEndpointWebExtension对象)的health方法。

沿着逻辑一直往下走,会走到

org.springframework.boot.actuate.health.HealthIndicator

的getHealth方法。但这个方法是个抽象方法,需要子类去覆盖。

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

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

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

并返回。

在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();    }}
本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » 源码分析:关于SpringBoot的健康检查

评论 抢沙发

8 + 4 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
×
订阅图标按钮