计一次生产一台服务器重启导致 Redis 集群两个实例宕机服务不可用问题分析排查。
Redis 集群宕机导致签权服务异常,进而导致所有服务不可用,其中还包含支付系统,是一次严重的生产事故。
恢复耗时半小时,电话被打爆,高层领导,现场项目经理,销售,测试都紧盯着问题修复。
背景
- 系统是基于微服务架构开发部署,统一的签权中心服务,网关服务。
Redis 集群,签权中心,网关服务等微服务的基础组件是作为基础服务,给所有业务系统接入。 - Redis 集群是在云服务器由运维搭建的 三主三从 集群。
- 微服务业务系统的框架统一使用的是基于 Spring Boot 2.2.6 版本集成好的基础框架开发。
问题
生产一台服务器重启导致 Redis 集群宕机所有业务服务不可用,一次严重的生产事故。
分析
服务器重启 Redis 实例没有重启。
原因:没有将 Redis 进程加入监控守护管理(Supervisor)。
解决:将 Redis 进程加入加入监控守护管理,系统重启后自动启动 Redis 进程。
一台服务重启,整个 Redis 集群不可用。
原因:Redis 集群是三主三从,存在一台服务器部署了两个 master 实例。
解决:增加服务器,三主三从 Redis 实例分别部署到单独的服务器上。
实验了主从切换后,出现业务系统连接失败。
原因:Spring Boot 默认使用的 Redis 客户端 Lettuce 没有动态刷新 Redis 集群节点的拓扑,当出现后仍一直连接其中一个节点导致超时。
解决:升级到 Spring Boot 2.3.0+ 版本,配置文件开启刷新 Redis 集群节点拓扑;不升级,手动添加刷新拓扑的 Class 配置。
Spring Boot 2.3.0+ 版本,支持属性配置项:
spring.redis.timeout=60s spring.redis.lettuce.cluster.refresh.adaptive=true|false spring.redis.lettuce.cluster.refresh.period=60s spring.redis.lettuce.cluster.refresh.dynamic-refresh-sources=true|false //版本 2.4.0+ 增加
Spring Boot 2.2.6 需要添加 Class 配置项:
import java.util.Objects; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.data.redis.LettuceClientConfigurationBuilderCustomizer; import org.springframework.boot.autoconfigure.data.redis.RedisProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration.LettuceClientConfigurationBuilder; import org.springframework.data.redis.core.RedisOperations; import com.clearofchina.core.util.ValidateUtil; import com.clearofchina.uaa.config.LettuceClusterProperties.Refresh; import io.lettuce.core.ClientOptions; import io.lettuce.core.ClientOptions.Builder; import io.lettuce.core.TimeoutOptions; import io.lettuce.core.cluster.ClusterClientOptions; import io.lettuce.core.cluster.ClusterTopologyRefreshOptions; /** * Lettuce针对Redis Cluster的自定义配置,参照springboot2.3.7封装 * */ @Configuration @ConditionalOnClass(RedisOperations.class) @EnableConfigurationProperties(value = { RedisProperties.class, LettuceClusterProperties.class }) public class RedisClusterTopologyRefreshBuilderCustomizer implements LettuceClientConfigurationBuilderCustomizer { @Autowired private LettuceClusterProperties lettuceClusterProperties; @Autowired private RedisProperties redisProperties; @Override public void customize(LettuceClientConfigurationBuilder clientConfigurationBuilder) { Builder topologyRefreshClientOptionsBuilder = initializeTopologyRefreshClientOptionsBuilder(); if (Objects.nonNull(topologyRefreshClientOptionsBuilder)) { clientConfigurationBuilder.clientOptions( topologyRefreshClientOptionsBuilder.timeoutOptions(TimeoutOptions.enabled()).build()); } } /** * 添加定时刷新拓扑选项 * * @return */ private ClientOptions.Builder initializeTopologyRefreshClientOptionsBuilder() { if (Objects.nonNull(redisProperties) && Objects.nonNull(redisProperties.getCluster()) && ValidateUtil.isNotEmpty(redisProperties.getCluster().getNodes())) { ClusterClientOptions.Builder builder = ClusterClientOptions.builder(); Refresh refreshProperties = lettuceClusterProperties.getRefresh(); ClusterTopologyRefreshOptions.Builder refreshBuilder = ClusterTopologyRefreshOptions.builder(); if (refreshProperties.getPeriod() != null) { refreshBuilder.enablePeriodicRefresh(refreshProperties.getPeriod()); } if (refreshProperties.isAdaptive()) { refreshBuilder.enableAllAdaptiveRefreshTriggers(); } return builder.topologyRefreshOptions(refreshBuilder.build()); } return null; } }
参考:
更多内容请访问:IT源点
注意:本文归作者所有,未经作者允许,不得转载