Cacheable redis 宕机

本文详细介绍了在使用Spring Boot和Redis进行数据缓存时,如何配置异常处理机制以确保即使Redis服务器出现问题,应用也能继续运行并从数据库获取数据。通过自定义错误处理器,实现了在Redis异常时的日志记录及程序继续执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用Cacheable注解Redis方法时,如果Redis服务器挂了,就直接抛出异常了,
java.net.ConnectException: Connection refused: connect

那么,有没有什么办法可以继续向下执行方法,从相关的数据库中查询数据,而不是直接抛出异常导致整个程序终止运行呢?

经过反复翻看Spring的源码和相关资料,并经过不断验证,得出了答案:有相关的方案!!!

原文:https://blog.youkuaiyun.com/l1028386804/article/details/82597154

<dependency>
    <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
</dependency>

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>
# Redis服务器地址

spring.redis.host=192.168.2.2
# Redis服务器连接端口
spring.redis.port=6379
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1
# 连接超时时间(毫秒)
spring.redis.timeout=1000
package org.springframework.cache.annotation;
 
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.CacheResolver;
import org.springframework.cache.interceptor.KeyGenerator;
 
/**
 * An implementation of {@link CachingConfigurer} with empty methods allowing
 * sub-classes to override only the methods they're interested in.
 *
 * @author Stephane Nicoll
 * @since 4.1
 * @see CachingConfigurer
 */
public class CachingConfigurerSupport implements CachingConfigurer {
 
    @Override
    public CacheManager cacheManager() {
        return null;
    }
 
    @Override
    public KeyGenerator keyGenerator() {
        return null;
    }
 
    @Override
    public CacheResolver cacheResolver() {
        return null;
    }
 
    @Override
    public CacheErrorHandler errorHandler() {
        return null;
    }
 
}
package com.gxkj.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.Cache;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.lang.reflect.Method;

/**
 * Created by yangqj on 2017/4/30.
 */
@Configuration
@EnableCaching
@EnableAspectJAutoProxy(exposeProxy = true)
@Slf4j
public class RedisConfig extends CachingConfigurerSupport {
    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private int port;

    @Value("${spring.redis.timeout}")
    private int timeout;

    @Value("${spring.redis.jedis.pool.max-idle}")
    private int maxIdle;

    @Value("${spring.redis.jedis.pool.max-wait}")
    private long maxWaitMillis;

    @Bean
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object o, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(o.getClass().getName());
                sb.append("-");
                sb.append(method.getName());
                sb.append("-");
                for (Object param : params) {
                    sb.append(param.toString());
                }
                return sb.toString();
            }
        };
    }

    /**
     * 设置@cacheable 序列化方式
     *
     * @return
     */
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration() {
        RedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
        RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
        configuration = configuration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer));
        return configuration;
    }


    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(factory);

        // key序列化方式;(不然会出现乱码;),但是如果方法上有Long等非String类型的话,会报类型转换错误;
        // 所以在没有自己定义key生成策略的时候,以下这个代码建议不要这么写,可以不配置或者自己实现ObjectRedisSerializer
        // 或者JdkSerializationRedisSerializer序列化方式;
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());

        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        return redisTemplate;
    }

    @Bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);

        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

        return template;
    }

    /**
     * redis数据操作异常处理 这里的处理:在日志中打印出错误信息,但是放行
     * 保证redis服务器出现连接等问题的时候不影响程序的正常运行,使得能够出问题时不用缓存
     *
     * @return
     */
    @Bean
    @Override
    public CacheErrorHandler errorHandler() {
        CacheErrorHandler cacheErrorHandler = new CacheErrorHandler() {

            @Override
            public void handleCachePutError(RuntimeException exception, Cache cache,
                                            Object key, Object value) {
                RedisErrorException(exception, key);
            }

            @Override
            public void handleCacheGetError(RuntimeException exception, Cache cache,
                                            Object key) {
                RedisErrorException(exception, key);
            }

            @Override
            public void handleCacheEvictError(RuntimeException exception, Cache cache,
                                              Object key) {
                RedisErrorException(exception, key);
            }

            @Override
            public void handleCacheClearError(RuntimeException exception, Cache cache) {
                RedisErrorException(exception, null);
            }
        };
        return cacheErrorHandler;
    }

    protected void RedisErrorException(Exception exception,Object key){
        log.error("redis异常:key=[{}]", key, exception);
    }


}

转载于:https://www.cnblogs.com/zhizhao/p/10151287.html

<think>首先,用户的问题是关于Java Spring Boot应用中设置Redis操作超时时间的原因。用户说:"我想理解在Java Spring Boot应用中为何为Redis操作设置超时时间 请问Java Spring Boot Redis 设置超时时间的原因"。所以,我需要解释为什么在Spring Boot应用中使用Redis时需要设置超时时间。 回顾系统级指令: - 所有行内数学表达式必须使用$...$格式,但在这个问题中,可能没有数学表达式,所以我可以忽略。 - 独立公式必须使用$$...$$格式并单独成段,同样,可能不适用。 - LaTeX语法正确:确保任何数学公式正确。 - 使用中文回答:整个回答要用中文。 - 生成相关问题:在回答结束后,添加"§§相关问题§§"部分,列出3个相关问题。 - 回答中引用的段落末尾自然地添加引用标识:引用用户提供的引用内容,并在末尾添加[^1]、[^2]等。 用户提供的引用: - [^1]: 关于Spring Cache集成Redis设置过期时间。 - [^2]: 解决Redis超时问题Command timed out。 - [^3]: Spring Boot Redis访问超时,设置Session失效时间。 - [^4]: Java Redis连接超时Command timed out。 这些引用都与Redis超时问题相关,所以我需要整合这些信息来回答用户的问题。 关键点:为什么设置超时时间? - 防止操作阻塞:如果Redis服务器响应慢或不可用,设置超时时间可以防止Java应用无限期等待,导致线程阻塞。 - 资源管理:避免长时间运行的操作消耗系统资源,比如连接池耗尽。 - 提高系统稳定性:超时机制可以快速失败,允许应用处理错误或重试,而不是起。 - 网络问题:在网络不稳定的情况下,超时设置可以处理连接超时或命令超时。 - 具体在Spring Boot中:通过配置如Lettuce或Jedis的timeout属性来设置。 从引用中提取: - [^1]:设置过期时间可能是针对缓存数据,但用户问的是操作超时,即连接或命令超时。 - [^2]:提到Redis操作一段时间不操作后超时,可能是连接池问题或Redis服务器配置。 - [^3]:设置Session失效时间,但用户问的是操作超时,不是缓存过期。 - [^4]:Java连接Redis时出现Command timed out错误,需要设置超时时间。 用户的问题焦点是"操作超时时间",即连接或命令执行超时,而不是缓存数据过期时间。所以我需要区分清楚。 回答结构: 1. **解释原因**:清晰、结构化的解释为什么设置超时时间。 2. **参考引用**:自然地融入引用内容。 3. **生成相关问题**:在结束时添加相关问题。 结构化回答: - 引言:简要概述原因。 - 主要原因:列出2-3个关键原因。 - 如何设置:简要提及如何在Spring Boot中设置(可选)。 - 结束语:总结重要性。 - 相关问题:添加3个相关问题。 确保回答真实可靠:基于Redis和Spring Boot的常识。 最终回答用中文。</think>在Java Spring Boot应用中为Redis操作设置超时时间(也称为命令超时或连接超时)是确保系统健壮性和稳定性的关键机制。Redis作为一种高性能的内存数据库,常用于缓存、会话管理或数据存储。然而,在分布式环境中,网络延迟、服务器负载或资源瓶颈可能导致Redis操作(如连接、读取或写入)长时间阻塞,从而引发应用性能下降甚至崩溃。设置超时时间后,如果操作在指定时间内未完成(例如2000毫秒),应用会主动中断并抛出异常(如`RedisCommandTimeoutException`),避免无限期等待。下面我将逐步解释主要原因,并结合实际场景说明。 ### 主要原因 1. **防止线程阻塞和资源耗尽**: - 在Spring Boot应用中,Redis操作通常通过连接池(如Lettuce或Jedis)管理多个连接。如果Redis服务器响应缓慢或网络不稳定,操作可能起,导致应用线程被长时间占用。例如,一个高并发场景中,多个线程同时等待Redis响应,可能耗尽连接池资源(如连接池满),进而引发连锁故障(如服务不可用)。 - 引用[^2]中提到:"一段时间不操作redis,再次操作时,就会出现超时问题Command timed out",这往往是由于连接池中的空闲连接超时失效,设置超时时间可以主动回收空闲连接,避免首次操作时阻塞。 2. **处理网络不稳定和服务器故障**: - 网络问题(如丢包或高延迟)或Redis服务宕机时,操作可能无法及时响应。设置超时时间(如连接超时`connect-timeout`和命令超时`timeout`)允许应用快速失败(fail-fast),转而执行降级策略(如返回默认值或重试机制),提升用户体验和系统容错性。 - 引用[^4]指出:"Command timed out"错误常发生在网络环境不佳时,设置超时时间能强制中断长时间未响应的命令,避免应用起[^4]。 3. **优化系统性能和资源管理**: - 超时设置有助于识别性能瓶颈。例如,在缓存场景中,如果Redis询超时,应用可以回退到数据库或其他后备源,防止单点故障影响整体性能。此外,结合Spring Cache(如`@Cacheable`注解),设置超时时间可以控制缓存操作的时效性,避免过期数据占用资源。 - 引用[^1]强调通过集成Redis设置过期时间来管理缓存生命周期,但操作超时是更底层的保障机制,确保单个命令不会无限期阻塞[^1]。引用[^3]也提到在Session管理中设置失效时间(`maxInactiveIntervalInSeconds`),但操作超时是防止实时命令执行超时的补充措施[^3]。 ### 如何在Spring Boot中设置超时时间 在Spring Boot中,可以通过配置文件(如`application.yml`)或代码配置Redis超时参数。常用属性包括: - `spring.redis.timeout`:命令执行超时时间(单位:毫秒)。 - `spring.redis.lettuce.pool.max-wait`:连接池等待超时时间。 示例配置: ```yaml spring: redis: host: localhost port: 6379 timeout: 2000 # 命令超时时间设置为2秒 lettuce: pool: max-active: 8 max-wait: 1000 # 连接池等待超时1秒 ``` 如果超时问题频繁发生,建议检网络状况、Redis服务器性能或调整连接池参数。 ### 总结 设置Redis操作超时时间的主要目的是避免应用因外部依赖(Redis)的延迟或故障而陷入不可控状态,从而提升系统的可靠性、响应速度和资源利用率。在Spring Boot中,合理配置超时参数(如2000-5000毫秒)是预防常见问题(如`Command timed out`)的最佳实践。如果未设置超时,可能导致应用线程死锁、请求堆积甚至服务雪崩,因此它是分布式系统设计中的基础防御措施[^2][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值