@cacheable注解redis时,redis宕机或其他原因连不上,继续调用原方法的解决方案
在spring boot应用中,我们经常使用@cacheable注解来缓存数据,以提高应用的性能。当选择redis作为缓存存储时,如果redis服务因某种原因不可用(如宕机、网络问题等),默认情况下,@cacheable注解会抛出异常,导致整个请求失败。本文将探讨如何在redis不可用时,让@cacheable注解继续调用原方法,确保服务的可用性和稳定性。
1. 问题背景
1.1 @cacheable注解的基本使用
@cacheable是spring框架提供的一个注解,用于标识一个方法的结果需要被缓存。当该方法被调用时,spring会先检查缓存中是否存在对应的数据,如果存在,则直接返回缓存中的数据;如果不存在,则执行方法并将结果存入缓存。
1.2 redis宕机的影响
当redis服务宕机或网络连接出现问题时,@cacheable注解尝试访问redis时会抛出异常,例如org.springframework.data.redis.redisconnectionfailureexception。这会导致方法调用失败,影响用户体验和系统稳定性。
2. 解决方案
2.1 使用自定义异常处理器
可以通过自定义异常处理器来捕获redis连接异常,并在捕获到异常时继续调用原方法。具体步骤如下:
2.1.1 创建自定义异常处理器
首先,创建一个自定义异常处理器类,用于处理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属性,可以在缓存操作成功后决定是否将结果存入缓存。虽然这个属性不能直接解决redis宕机的问题,但可以结合其他逻辑来实现类似的效果。
2.3 使用@cacheable的cache-null-values属性
设置@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不可用时,可以采用降级策略,例如从数据库中直接获取数据。这可以通过自定义的缓存管理器来实现。
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注解继续调用原方法,从而提高系统的稳定性和可用性。具体实现方式包括自定义异常处理器、使用unless和cache-null-values属性、以及降级策略。在使用spring框架结合redis实现缓存功能时,如果redis宕机或由于其他原因导致连接不上redis,可以通过配置cachemanager来实现当缓存不可用时自动回退到原始方法的调用。这样可以保证系统的可用性和稳定性。
以下是一个具体的实现示例:
添加依赖:首先确保你的项目中已经添加了spring boot和redis的相关依赖。
<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:创建一个自定义的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)) // 设置默认过期时间
.serializevalueswith(redisserializationcontext.serializationpair.fromserializer(new genericjackson2jsonredisserializer()));
map<string, rediscacheconfiguration> cacheconfigurations = new hashmap<>();
cacheconfigurations.put("mycache", config);
return new fallbackrediscachemanager(redisconnectionfactory, config, cacheconfigurations);
}
}实现fallbackrediscachemanager:创建一个自定义的cachemanager,在redis不可用时回退到内存缓存。
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) {
// 模拟数据获取过程
system.out.println("fetching data from database for id: " + id);
return "data for id: " + id;
}
}通过上述配置,当redis不可用时,fallbackrediscachemanager会捕获到redisconnectionfailureexception异常,并回退到内存缓存。这样可以确保即使redis宕机,系统仍然能够正常运行并返回数据。在使用spring cache与redis结合时,如果redis出现宕机或连接问题,可以通过配置cachemanager和实现自定义的cacheerrorhandler来确保即使缓存不可用,业务逻辑也能正常运行。以下是一个详细的解决方案示例:
1. 添加依赖
首先,确保你的项目中已经添加了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)) // 设置缓存过期时间为60分钟
.disablecachingnullvalues();
return rediscachemanager.builder(connectionfactory)
.cachedefaults(config)
.build();
}
}4. 实现自定义的cacheerrorhandler
实现自定义的cacheerrorhandler,以便在缓存操作失败时进行处理:
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) {
// 模拟从数据库获取用户信息
system.out.println("fetching user from database: " + userid);
return new user(userid, "john doe");
}
}6. 测试
你可以通过模拟redis宕机或断开连接来测试上述配置是否生效。例如,可以临时关闭redis服务,然后调用getuserbyid方法,查看是否能够正常返回数据。
总结
通过以上配置,当redis不可用时,cacheerrorhandler会捕获到缓存操作的异常,并打印错误信息。同时,由于@cacheable注解的默认行为是当缓存不可用时直接调用原方法,因此业务逻辑不会受到影响。这样可以确保系统的高可用性和稳定性。
到此这篇关于使用@cacheable注解redis时redis宕机或其他原因连不上继续调用原方法的解决方案的文章就介绍到这了,更多相关@cacheable注解 redis宕机内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论