SpringCloud 系列 --2. 服务实例健康自检 SpringBoot Actuator

本贴最后更新于 1860 天前,其中的信息可能已经渤澥桑田

说明:
在默认情况下,Eureka 的客户端每隔 30 秒会发送一次心跳给服务端,告知仍然存活,但是一些情况下(比如数据库挂了),客户端表面上可以正常发送心跳,但实际上无法提供服务。

这时可以利用 Eureka 的健康检查控制器(哪个模块对外提供服务需要自检,在哪个模块实现)。

需要在 pom.xml 中引入

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-actuator</artifactId> <version>1.5.3.RELEASE</version> </dependency>

SpringBoot Actuator

第一步: 可以实现一个自定义的 HealthIndicator 来根据是否能访问数据库决定自身的健康状态,

package org.crazyit.cloud; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.Status; import org.springframework.boot.actuate.health.Health.Builder; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.stereotype.Component; /** * 健康指示器 * @author 杨恩雄 * */ @Component public class MyHealthIndicator implements HealthIndicator { @Override public Health health() { if(HealthController.canVisitDb) { // 成功连接数据库,返回UP return new Health.Builder(Status.UP).build(); } else { // 连接数据库失败,返回 out of service return new Health.Builder(Status.DOWN).build(); } } }

模拟数据库状态用一个控制器来控制 HealthController 中的静态变量 canVisitDb

package org.crazyit.cloud; import javax.servlet.http.HttpServletRequest; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController public class HealthController { // 标识当前数据库是否可以访问 static Boolean canVisitDb = false; @RequestMapping(value = "/db/{canVisitDb}", method = RequestMethod.GET) @ResponseBody public String setConnectState(@PathVariable("canVisitDb") Boolean canVisitDb) { this.canVisitDb = canVisitDb; return "当前数据库是否正常: " + this.canVisitDb; } }

第二步: 服务提供者把自身健康状态告知服务器.

实现"健康检查处理器", 将应用的健康状态保存在内存中,状态一旦发生改变,就会重新向服务器进行注册,其他的客户端将拿不到这些不可用的实例。

package org.crazyit.cloud; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.health.Status; import org.springframework.stereotype.Component; import com.netflix.appinfo.HealthCheckHandler; import com.netflix.appinfo.InstanceInfo.InstanceStatus; /** * 健康检查处理器 * @author 杨恩雄 * */ @Component public class MyHealthCheckHandler implements HealthCheckHandler { @Autowired private MyHealthIndicator indicator; public InstanceStatus getStatus(InstanceStatus currentStatus) { System.out.println("Eureka定时器调用了哈,每隔eureka.client.instanceInfoReplicationIntervalSeconds时间调用一次 。。。。。。。"); Status s = indicator.health().getStatus(); if(s.equals(Status.UP)) { System.out.println("数据库正常连接"); return InstanceStatus.UP; } else { System.out.println("数据库无法连接"); return InstanceStatus.DOWN; } } }

上述代码根据健康指示器结果来返回不通的状态。 Eureka 会启动一个定时器,定时刷新本地实例的信息,并且执行"处理器"中的 getStatus 方法,再将服务实例状态"更新"到服务器中, 默认定时器每隔 30 秒执行一次,可以通过修改配置 eureka.client.instanceInfoReplicationIntervalSeconds 来改变,如下:

spring: application: name: health-handler-provider eureka: instance: hostname: localhost client: healthcheck: enabled: true instanceInfoReplicationIntervalSeconds: 10 serviceUrl: defaultZone: http://localhost:8761/eureka/

因为默认数据库 canVisitDb = false;所有服务默认是 DOWN 的状态
image.png

访问 http://localhost:8080/db/true 将数据库状态设置为 true
image.png

再看状态就是 UP

image.png

第三步 服务查询

查看集群中的服务,可以使用 SpringCloud 的 discoveryClient 类, 或者 eurekaClient 类,SpringCloud 对 Eureka 进行了封装。本例中使用 discoveryClient 的方法来查询服务实例,输出服务实例的状态等信息。

package org.crazyit.cloud; import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient.EurekaServiceInstance; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import com.netflix.appinfo.InstanceInfo; @RestController @Configuration public class InvokerController { @Autowired private DiscoveryClient discoveryClient; @RequestMapping(value = "/router", method = RequestMethod.GET) @ResponseBody public String router() { // 查找服务列表 List<ServiceInstance> ins = getServiceInstances(); // 输出服务信息及状态 for (ServiceInstance service : ins) { EurekaServiceInstance esi = (EurekaServiceInstance) service; InstanceInfo info = esi.getInstanceInfo(); System.out.println(info.getAppName() + "---" + info.getInstanceId() + "---" + info.getStatus()); } return ""; } /** * 查询可用服务 */ private List<ServiceInstance> getServiceInstances() { List<String> ids = discoveryClient.getServices(); List<ServiceInstance> result = new ArrayList<ServiceInstance>(); for (String id : ids) { List<ServiceInstance> ins = discoveryClient.getInstances(id); result.addAll(ins); } return result; } }

1.访问服务 http://localhost:9000/router, 输出如下(说明此时 db 正常):
HEALTH-HANDLER-PROVIDER---172.16.112.115:health-handler-provider---UP
HEALTH-HANDLER-INVOKER---172.16.112.115:health-handler-invoker:9000---UP

  1. 访问 http://localhost:8080/db/false 将 db 设置为 false
    3.再访问 http://localhost:9000/router 服务 输出如下:
    HEALTH-HANDLER-INVOKER---172.16.112.115:health-handler-invoker:9000---UP
    说明,可用的服务只剩下调用者自己了,服务的提供者已经不存在于服务列表中.

综上,

想要服务健康自检,需要两步(实测,仅需要第二步实现 HealthCheckHandler):
1.实现 HealthIndicator 来返回服务的状态。

package org.crazyit.cloud; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.Status; import org.springframework.boot.actuate.health.Health.Builder; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.stereotype.Component; /** * 健康指示器 * @author 杨恩雄 * */ @Component public class MyHealthIndicator implements HealthIndicator { @Override public Health health() { if(HealthController.canVisitDb) { // 成功连接数据库,返回UP return new Health.Builder(Status.UP).build(); } else { // 连接数据库失败,返回 out of service return new Health.Builder(Status.DOWN).build(); } } }
  1. 实现 HealthCheckHandler 来让 Eureka 调用获取 getStatus 方法来获取状态并通知服务端
package org.crazyit.cloud; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.health.Status; import org.springframework.stereotype.Component; import com.netflix.appinfo.HealthCheckHandler; import com.netflix.appinfo.InstanceInfo.InstanceStatus; /** * 健康检查处理器 * @author 杨恩雄 * */ @Component public class MyHealthCheckHandler implements HealthCheckHandler { @Autowired private MyHealthIndicator indicator; public InstanceStatus getStatus(InstanceStatus currentStatus) { System.out.println("Eureka定时器调用了哈,每隔eureka.client.instanceInfoReplicationIntervalSeconds时间调用一次 。。。。。。。"); Status s = indicator.health().getStatus(); if(s.equals(Status.UP)) { System.out.println("数据库正常连接"); return InstanceStatus.UP; } else { System.out.println("数据库无法连接"); return InstanceStatus.DOWN; } } }
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    948 引用 • 1460 回帖
  • Health
    2 引用

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...