使用@Cacheable注解Redis時Redis宕機(jī)或其他原因連不上繼續(xù)調(diào)用原方法的解決方案
@Cacheable注解Redis時,Redis宕機(jī)或其他原因連不上,繼續(xù)調(diào)用原方法的解決方案
在Spring Boot應(yīng)用中,我們經(jīng)常使用??@Cacheable??注解來緩存數(shù)據(jù),以提高應(yīng)用的性能。當(dāng)選擇Redis作為緩存存儲時,如果Redis服務(wù)因某種原因不可用(如宕機(jī)、網(wǎng)絡(luò)問題等),默認(rèn)情況下,??@Cacheable??注解會拋出異常,導(dǎo)致整個請求失敗。本文將探討如何在Redis不可用時,讓??@Cacheable??注解繼續(xù)調(diào)用原方法,確保服務(wù)的可用性和穩(wěn)定性。
1. 問題背景
1.1 ??@Cacheable??注解的基本使用
??@Cacheable??是Spring框架提供的一個注解,用于標(biāo)識一個方法的結(jié)果需要被緩存。當(dāng)該方法被調(diào)用時,Spring會先檢查緩存中是否存在對應(yīng)的數(shù)據(jù),如果存在,則直接返回緩存中的數(shù)據(jù);如果不存在,則執(zhí)行方法并將結(jié)果存入緩存。
1.2 Redis宕機(jī)的影響
當(dāng)Redis服務(wù)宕機(jī)或網(wǎng)絡(luò)連接出現(xiàn)問題時,??@Cacheable??注解嘗試訪問Redis時會拋出異常,例如??org.springframework.data.redis.RedisConnectionFailureException??。這會導(dǎo)致方法調(diào)用失敗,影響用戶體驗(yàn)和系統(tǒng)穩(wěn)定性。
2. 解決方案
2.1 使用自定義異常處理器
可以通過自定義異常處理器來捕獲Redis連接異常,并在捕獲到異常時繼續(xù)調(diào)用原方法。具體步驟如下:
2.1.1 創(chuàng)建自定義異常處理器
首先,創(chuàng)建一個自定義異常處理器類,用于處理Redis連接異常。
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.CacheErrorHandler;
public class CustomCacheErrorHandler implements CacheErrorHandler {
@Override
public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
// 處理讀取緩存時的異常
System.out.println("Cache get error: " + exception.getMessage());
}
@Override
public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
// 處理寫入緩存時的異常
System.out.println("Cache put error: " + exception.getMessage());
}
@Override
public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
// 處理清除緩存時的異常
System.out.println("Cache evict error: " + exception.getMessage());
}
@Override
public void handleCacheClearError(RuntimeException exception, Cache cache) {
// 處理清空緩存時的異常
System.out.println("Cache clear error: " + exception.getMessage());
}
}2.1.2 配置自定義異常處理器
在Spring Boot配置文件中,配置自定義的異常處理器。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.cache.annotation.EnableCaching;
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CustomCacheErrorHandler customCacheErrorHandler() {
return new CustomCacheErrorHandler();
}
}2.2 使用??@Cacheable??的??unless??屬性
??@Cacheable??注解提供了一個??unless??屬性,可以在緩存操作成功后決定是否將結(jié)果存入緩存。雖然這個屬性不能直接解決Redis宕機(jī)的問題,但可以結(jié)合其他邏輯來實(shí)現(xiàn)類似的效果。
2.3 使用??@Cacheable?的??cache-null-values??屬性
設(shè)置??@Cacheable??注解的??cache-null-values??屬性為??false??,這樣即使Redis不可用,也不會將??null??值存入緩存。
@Cacheable(value = "myCache", cacheNullValues = false)
public User getUserById(Long id) {
return userRepository.findById(id).orElse(null);
}2.4 使用降級策略
在Redis不可用時,可以采用降級策略,例如從數(shù)據(jù)庫中直接獲取數(shù)據(jù)。這可以通過自定義的緩存管理器來實(shí)現(xiàn)。
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Optional;
@Configuration
public class CustomCacheManager {
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("myCache") {
@Override
public Cache getCache(String name) {
Cache cache = super.getCache(name);
if (cache == null) {
// 如果Redis不可用,使用本地緩存
cache = new ConcurrentMapCache(name);
}
return cache;
}
};
}
}我們可以在Redis不可用時,確保??@Cacheable???注解繼續(xù)調(diào)用原方法,從而提高系統(tǒng)的穩(wěn)定性和可用性。具體實(shí)現(xiàn)方式包括自定義異常處理器、使用??unless???和??cache-null-values??屬性、以及降級策略。在使用Spring框架結(jié)合Redis實(shí)現(xiàn)緩存功能時,如果Redis宕機(jī)或由于其他原因?qū)е逻B接不上Redis,可以通過配置??CacheManager??來實(shí)現(xiàn)當(dāng)緩存不可用時自動回退到原始方法的調(diào)用。這樣可以保證系統(tǒng)的可用性和穩(wěn)定性。
以下是一個具體的實(shí)現(xiàn)示例:
添加依賴:首先確保你的項(xiàng)目中已經(jīng)添加了Spring Boot和Redis的相關(guān)依賴。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>配置Redis連接:在application.properties中配置Redis連接信息。
spring.redis.host=localhost spring.redis.port=6379
自定義CacheManager:創(chuàng)建一個自定義的CacheManager,在其中處理Redis不可用的情況。
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1)) // 設(shè)置默認(rèn)過期時間
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
cacheConfigurations.put("myCache", config);
return new FallbackRedisCacheManager(redisConnectionFactory, config, cacheConfigurations);
}
}實(shí)現(xiàn)FallbackRedisCacheManager:創(chuàng)建一個自定義的CacheManager,在Redis不可用時回退到內(nèi)存緩存。
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFailureException;
import java.util.Arrays;
import java.util.List;
public class FallbackRedisCacheManager extends RedisCacheManager {
private final CacheManager fallbackCacheManager;
public FallbackRedisCacheManager(RedisConnectionFactory connectionFactory, RedisCacheConfiguration defaultCacheConfiguration, Map<String, RedisCacheConfiguration> initialCacheConfigurations) {
super(connectionFactory, defaultCacheConfiguration, initialCacheConfigurations);
SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
simpleCacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("fallbackCache")));
simpleCacheManager.afterPropertiesSet();
this.fallbackCacheManager = simpleCacheManager;
}
@Override
public Cache getCache(String name) {
try {
return super.getCache(name);
} catch (RedisConnectionFailureException e) {
return fallbackCacheManager.getCache(name);
}
}
}使用@Cacheable注解:在需要緩存的方法上使用@Cacheable注解。
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class MyService {
@Cacheable(value = "myCache", key = "#id")
public String getData(String id) {
// 模擬數(shù)據(jù)獲取過程
System.out.println("Fetching data from database for ID: " + id);
return "Data for ID: " + id;
}
}通過上述配置,當(dāng)Redis不可用時,??FallbackRedisCacheManager??會捕獲到??RedisConnectionFailureException??異常,并回退到內(nèi)存緩存。這樣可以確保即使Redis宕機(jī),系統(tǒng)仍然能夠正常運(yùn)行并返回?cái)?shù)據(jù)。在使用Spring Cache與Redis結(jié)合時,如果Redis出現(xiàn)宕機(jī)或連接問題,可以通過配置??CacheManager??和實(shí)現(xiàn)自定義的??CacheErrorHandler??來確保即使緩存不可用,業(yè)務(wù)邏輯也能正常運(yùn)行。以下是一個詳細(xì)的解決方案示例:
1. 添加依賴
首先,確保你的項(xiàng)目中已經(jīng)添加了Spring Boot Starter Cache和Spring Boot Starter Data Redis的依賴:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>2. 配置RedisTemplate
配置??RedisTemplate??以使用JSON序列化方式存儲對象:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
}3. 配置CacheManager
配置??CacheManager??以使用Redis作為緩存存儲:
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import java.time.Duration;
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(60)) // 設(shè)置緩存過期時間為60分鐘
.disableCachingNullValues();
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.build();
}
}4. 實(shí)現(xiàn)自定義的CacheErrorHandler
實(shí)現(xiàn)自定義的??CacheErrorHandler??,以便在緩存操作失敗時進(jìn)行處理:
import org.springframework.cache.Cache;
import org.springframework.cache.Cache.ValueException;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.CacheErrorHandler;
@Configuration
public class CacheConfig extends CachingConfigurerSupport {
@Override
public CacheErrorHandler errorHandler() {
return new CacheErrorHandler() {
@Override
public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
// 處理緩存讀取錯誤
System.out.println("Cache get error: " + exception.getMessage());
}
@Override
public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
// 處理緩存寫入錯誤
System.out.println("Cache put error: " + exception.getMessage());
}
@Override
public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
// 處理緩存刪除錯誤
System.out.println("Cache evict error: " + exception.getMessage());
}
@Override
public void handleCacheClearError(RuntimeException exception, Cache cache) {
// 處理緩存清除錯誤
System.out.println("Cache clear error: " + exception.getMessage());
}
};
}
}5. 使用@Cacheable注解
在需要緩存的方法上使用??@Cacheable??注解:
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Cacheable(value = "users", key = "#userId")
public User getUserById(String userId) {
// 模擬從數(shù)據(jù)庫獲取用戶信息
System.out.println("Fetching user from database: " + userId);
return new User(userId, "John Doe");
}
}6. 測試
你可以通過模擬Redis宕機(jī)或斷開連接來測試上述配置是否生效。例如,可以臨時關(guān)閉Redis服務(wù),然后調(diào)用??getUserById??方法,查看是否能夠正常返回?cái)?shù)據(jù)。
總結(jié)
通過以上配置,當(dāng)Redis不可用時,??CacheErrorHandler??會捕獲到緩存操作的異常,并打印錯誤信息。同時,由于??@Cacheable??注解的默認(rèn)行為是當(dāng)緩存不可用時直接調(diào)用原方法,因此業(yè)務(wù)邏輯不會受到影響。這樣可以確保系統(tǒng)的高可用性和穩(wěn)定性。
到此這篇關(guān)于使用@Cacheable注解Redis時Redis宕機(jī)或其他原因連不上繼續(xù)調(diào)用原方法的解決方案的文章就介紹到這了,更多相關(guān)@Cacheable注解 Redis宕機(jī)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實(shí)現(xiàn)跳躍表(skiplist)的簡單實(shí)例
這篇文章主要介紹了Java編程中跳躍表的概念和實(shí)現(xiàn)原理,并簡要敘述了它的結(jié)構(gòu),具有一定參考價值,需要的朋友可以了解下。2017-09-09
Intellij 下 mybatis 插件 MyBatisCodeHelperPro破解步驟詳解
這篇文章主要介紹了Intellij 下 mybatis 插件 MyBatisCodeHelperPro破解步驟,本文分步驟給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09
JAVA實(shí)現(xiàn)Token自動續(xù)期機(jī)制的示例代碼
本文主要介紹了JAVA實(shí)現(xiàn)Token自動續(xù)期機(jī)制的示例代碼,通過動態(tài)調(diào)整會話生命周期平衡安全性與用戶體驗(yàn),解決固定有效期Token帶來的風(fēng)險(xiǎn)與不便,感興趣的可以了解一下2025-09-09
Java執(zhí)行hadoop的基本操作實(shí)例代碼
這篇文章主要介紹了Java執(zhí)行hadoop的基本操作實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-04-04
Struts2通過自定義標(biāo)簽實(shí)現(xiàn)權(quán)限控制的方法
這篇文章主要介紹了Struts2通過自定義標(biāo)簽實(shí)現(xiàn)權(quán)限控制的方法,介紹了定義Struts2的自定義標(biāo)簽的三個步驟以及詳細(xì)解釋,需要的朋友可以參考下。2017-09-09
IDEA中Maven Dependencies出現(xiàn)紅色波浪線的原因及解決方法
在使用 IntelliJ IDEA 開發(fā) Java 項(xiàng)目時,尤其是基于 Maven 的項(xiàng)目,您可能會遇到 Maven Dependencies 中出現(xiàn)紅色波浪線的問題,這通常意味著項(xiàng)目的依賴無法被正確解析或下載,本文將詳細(xì)介紹該問題的原因及解決方法,并附有圖文說明,需要的朋友可以參考下2025-06-06
win10和win7下java開發(fā)環(huán)境配置教程
這篇文章主要為大家詳細(xì)介紹了win7下Java開發(fā)環(huán)境配置教程,win10下Java開發(fā)環(huán)境配置,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-06-06
java實(shí)現(xiàn)多數(shù)據(jù)源切換方式
本文介紹實(shí)現(xiàn)多數(shù)據(jù)源切換的四步方法:導(dǎo)入依賴、配置文件、啟動類注解、使用@DS標(biāo)記mapper和服務(wù)層,通過注解實(shí)現(xiàn)數(shù)據(jù)源動態(tài)切換,適用于實(shí)際開發(fā)中的多數(shù)據(jù)源場景2025-08-08

