问题
spring-boot 1.5.4升级到spring-boot2.0.6后,部署服务到环境,如果redis host或者port配置错误,会导致服务一直是unhealthy状态,查看服务日志没有错误信息,访问管理端口/actuator/health,只返回{"status":"DOWN"}
,给排查错误带来很大工作量。
原因
在spring-boot 1.5.4版本,redis client使用的是Jedis直连的方式,是阻塞式的,假如redis host配置错误,健康检查会报出警告如下:
2019-07-25 11:24:55.471 WARN 3130 --- [-10.211.108.214] o.s.b.a.health.RedisHealthIndicator : Health check failed
org.springframework.data.redis.RedisConnectionFailureException: Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
......
具体实现在spring-boot-actuator包中的AbstractHealthIndicator
类
@Override
public final Health health() {
Health.Builder builder = new Health.Builder();
try {
//尝试连接redis
doHealthCheck(builder);
}
catch (Exception ex) {
//log warn
this.logger.warn("Health check failed", ex);
builder.down(ex);
}
return builder.build();
}
但spring-boot 2.0.6 redis client默认使用的是Lettuce。
Lettuce是一种可伸缩,线程安全,完全非阻塞的Redis客户端,多个线程可以共享一个RedisConnection,它利用Netty NIO 框架来高效地管理多个连接,从而提供了异步和同步数据访问方式,用于构建非阻塞的反应性应用程序。
关于Lettuce和Jedis对比,可以参考这篇文章
由于使用的是Lettuce,健康检查使用的是reactive health check,具体实现在spring-boot-actuator的AbstractReactiveHealthIndicator
:
@Override
public final Mono<Health> health() {
try {
return doHealthCheck(new Health.Builder()).onErrorResume(this::handleFailure);
}
catch (Exception ex) {
return handleFailure(ex);
}
}
注意这里catch exception后没有log warn报出错误日志,导致如果redis配置错误,服务虽然不健康,但日志信息里也没有对应错误信息,这是spring-boot2.0的一个bug,会在spring-boot2.1版本中修复。bug对应的issue。
解决方案
配置health端口显示详细信息
虽然2.0版本无法从日志中获取到redis连接失败的日志信息,但可以如下配置让/health端口显示详细信息:
management.endpoint.health.show-details=always
然后访问 管理端口/actuator/health,即可查看服务down的详细原因了。
更换Jedis客户端
如果服务不是reactive响应式应用,可以更换回Jedis配置。阻塞式Jedis的健康检查可以抛出异常信息。