RPC(八:优雅启动)
上篇文章讲过一个新的服务实例,即使能够很快启动的实例,也不能马上被Eureka服务器发现,其他服务消费者需要一段时间,最长可能需要2分钟以上,才能够被发现和使用。
但是这种延迟暴露还是可能出现服务未完全启动就被其他服务访问到。
我们可以开启健康检查并将初始注册状态置为starting,这样当服务完全启动时才会注册到eureka。
不配置健康检查也行,就得等心跳来判断是否状态更新为up了。
eureka.client.healthcheck.enabled=true
eureka.instance.initial-status=starting
eureka的HealthChecks(健康检查)
eureka的默认的健康检查方式是heartbeat(心跳)。但是默认的heartbeat方式只会在注册时进行向eureka server(服务注册中心)发送eureka client的健康信息。这样一来就导致了两个问题:
1,eureka客户端的健康状态发生变化时在服务注册中心对应实例的状态不能得到更新。比如我们的一个eureka客户端的运行,这时在注册中心却不能意识到该服务已经停止。
2,eureka客户端运行正常,但是它所依赖的其他资源却挂掉了。比如Mysql数据库或者依赖的其他接口挂掉了。这时使用默认的heartbeat的方式也不能够检查出来。面对这种情况我们必须提供一种更加适应我们应用的健康检查方式。
要解决上述问题,默认可以通过配置eureka.client.healthcheck.enabled=true来完成。单独的配置该选项可以解决第一个问题但要想同时解决第二个问题我们必须同时实现HealthCheckHandler接口。代码如下:
package com.mfs.microweatherbasic.config;
import com.netflix.appinfo.HealthCheckHandler;
import com.netflix.appinfo.InstanceInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
@Component
public class MyHealthCheckHandler implements HealthCheckHandler {
@Value("${custom.service.weather-api}")
private String WEATHER_API;
@Autowired
private RestTemplate restTemplate;
private long time = 1;
@Override
public InstanceInfo.InstanceStatus getStatus(InstanceInfo.InstanceStatus currentStatus) {
System.out.println(time++);
ResponseEntity<String> entity = restTemplate.getForEntity(WEATHER_API + "?city=临沂", String.class);
if (time > 3) {
return InstanceInfo.InstanceStatus.DOWN;
}
if (entity.getStatusCodeValue() == 200) {
return InstanceInfo.InstanceStatus.UP;
} else {
return InstanceInfo.InstanceStatus.DOWN;
}
}
}
以上可以解决客户端启动完成后再注册到注册中心,暴露给其他服务。还可能出现这种情况,eureka服务器重启时,为启动完全,就被客户端拉取注册表,导致访问不到其他服务。如何解决?
服务器fetch-registry,register-with-eureka都设置为true;
eureka:
client:
fetch-registry: true
register-with-eureka: true// 将自身注册到Eureka 集群上面去
Eureka Server在服务刚刚启动的时候,可以获取注册信息(register-with-eureka: true时,他本身也作为客户端注册到Eureka上去了),然后注册到自身的服务上去。
这样当Eureka Server可以提供使用的时候,客户端拉取的注册表信息就比较完整了。
这样还是可能出现延迟暴露服务未完全启动就被其他客户端访问到。因此可以设置eureka.instance.initial-status=starting
简单学习下健康检查代码:
健康检查包括存活状态检查还有就绪状态检查,存活状态为 CORRECT 的时候 Status 才为 UP,就绪状态为 ACCEPTING_TRAFFIC 的时候 status 才为 UP。对应的 HealthIndicator 是:
public class ReadinessStateHealthIndicator extends AvailabilityStateHealthIndicator {
public ReadinessStateHealthIndicator(ApplicationAvailability availability) {
super(availability, ReadinessState.class, (statusMappings) -> {
//存活状态为 CORRECT 的时候 Status 才为 UP
statusMappings.add(ReadinessState.ACCEPTING_TRAFFIC, Status.UP);
statusMappings.add(ReadinessState.REFUSING_TRAFFIC, Status.OUT_OF_SERVICE);
});
}
}
public class ReadinessStateHealthIndicator extends AvailabilityStateHealthIndicator {
public ReadinessStateHealthIndicator(ApplicationAvailability availability) {
super(availability, ReadinessState.class, (statusMappings) -> {
//就绪状态为 ACCEPTING_TRAFFIC 的时候 status 才为 UP
statusMappings.add(ReadinessState.ACCEPTING_TRAFFIC, Status.UP);
statusMappings.add(ReadinessState.REFUSING_TRAFFIC, Status.OUT_OF_SERVICE);
});
}
}
来源:Eureka Server集群重启问题追踪,实现微服务预热调用之后再开始服务
还有其他有关服务内部的优雅启动:
1,Spring Boot 中的 MVC Servlet 与 Web Service Servlet 的提前初始化(不使用懒加载)
MVC Servlet 指的就是 Spring-MVC 的 Servlet,其实就是提供 HTTP 服务的一些核心 Servlet,例如最核心的 org.springframework.web.servlet.DispatcherServlet。这些默认是懒加载的,需要通过下面的配置打开:
spring.mvc.servlet.load-on-startup: 1
除了 MVC Servlet,另一些 Servlet 可能是提供除了 HTTP 以外的其他应用协议的 Servlet,这些 Servlet 默认也是懒加载的,需要通过下面的配置打开:
spring.webservices.servlet.load-on-startup: 1
例如 WebSocket 的 org.springframework.ws.transport.http.MessageDispatcherServlet 就是通过这个配置进行初始化的。
2,spring-data-redis 连接池的初始化
连接池,最好初始化足够的连接数量,即配置 spring.redis.lettuce.pool.min-idle 以及 spring.redis.lettuce.pool.time-between-eviction-runs。
spring.redis.lettuce.pool.min-idle:连接池中最小的空闲连接数量,默认是 0
spring.redis.lettuce.pool.time-between-eviction-runs:common-pools 中 Evictor 初始执行延迟以及执行间隔。不配置的话,就没有启用 Evictor 任务。
来源:实现微服务预热调用之后再开始服务