@Cacheable annotation Redis, Redis is down or cannot connect to other reasons. Continue to call the original method solution
In Spring Boot applications, we often use@Cacheable
Annotations are used to cache data to improve application performance. When Redis is selected as cache storage, if the Redis service is unavailable for some reason (such as downtime, network problems, etc.), by default,@Cacheable
Annotation will throw an exception, causing the entire request to fail. This article will explore how to make@Cacheable
Annotation continues to call the original method to ensure the availability and stability of the service.
1. Problem background
1.1 Basic use of @Cacheable annotation
@Cacheable
is annotation provided by the Spring framework to identify the results of a method that need to be cached. When this method is called, Spring will first check whether the corresponding data exists in the cache. If it exists, it will directly return the data in the cache; if it does not exist, it will execute the method and store the result in the cache.
1.2 The impact of Redis downtime
When Redis service goes down or there is a problem with the network connection,@Cacheable
Annotation An exception is thrown when trying to access Redis, e.g.
. This will cause method calls to fail, affecting user experience and system stability.
2. Solution
2.1 Using a custom exception handler
You can catch Redis connection exceptions through a custom exception handler and continue to call the original method when the exception is caught. The specific steps are as follows:
2.1.1 Create a custom exception handler
First, create a custom exception handler class that handles Redis connection exceptions.
import ; import ; import ; public class CustomCacheErrorHandler implements CacheErrorHandler { @Override public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) { // Handle exceptions when reading cache ("Cache get error: " + ()); } @Override public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) { // Handle exceptions when writing to cache ("Cache put error: " + ()); } @Override public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) { // Handle exceptions when clearing cache ("Cache evict error: " + ()); } @Override public void handleCacheClearError(RuntimeException exception, Cache cache) { // Handle exceptions when clearing cache ("Cache clear error: " + ()); } }
2.1.2 Configuring a custom exception handler
In Spring Boot configuration file, configure a custom exception handler.
import ; import ; import ; @Configuration @EnableCaching public class CacheConfig { @Bean public CustomCacheErrorHandler customCacheErrorHandler() { return new CustomCacheErrorHandler(); } }
2.2 Using the unless attribute of @Cacheable
@Cacheable
Annotation provides aunless
Properties, you can decide whether to store the result in the cache after the cache operation is successful. Although this property cannot directly solve the problem of Redis downtime, similar effects can be achieved in combination with other logic.
2.3 Using the cache-null-values attribute of @Cacheable
Settings@Cacheable
Annotatedcache-null-values
Properties arefalse
, so that even if Redis is not available, it will not benull
The value is stored in the cache.
@Cacheable(value = "myCache", cacheNullValues = false) public User getUserById(Long id) { return (id).orElse(null); }
2.4 Using the downgrade strategy
When Redis is not available, a downgrade strategy can be adopted, such as getting data directly from the database. This can be achieved through a custom cache manager.
import ; import ; import ; import ; import ; import ; @Configuration public class CustomCacheManager { @Bean public CacheManager cacheManager() { return new ConcurrentMapCacheManager("myCache") { @Override public Cache getCache(String name) { Cache cache = (name); if (cache == null) { // If Redis is not available, use local cache cache = new ConcurrentMapCache(name); } return cache; } }; } }
We can ensure that when Redis is not available@Cacheable
Annotation continues to call the original method, thereby improving the stability and usability of the system. Specific implementation methods include custom exception processors and usageunless
andcache-null-values
Properties, and downgrade policies. When using the Spring framework to combine Redis to implement cache function, if Redis fails or cannot connect to Redis due to other reasons, you can configure itCacheManager
To implement calls that automatically fall back to the original method when the cache is unavailable. This ensures the availability and stability of the system.
Here is a specific implementation example:
Add dependencies: First make sure that you have added Spring Boot and Redis related dependencies to your project.
<dependency> <groupId></groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId></groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency>
Configure Redis connection:existConfigure Redis connection information in the
=localhost =6379
Custom CacheManager: Create a custom oneCacheManager
, where to deal with cases where Redis is not available.
import ; import ; import ; import ; import ; import ; import ; import ; import .GenericJackson2JsonRedisSerializer; import ; import ; import ; import ; @Configuration @EnableCaching public class CacheConfig { @Bean public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { RedisCacheConfiguration config = () .entryTtl((1)) // Set the default expiration time .serializeValuesWith((new GenericJackson2JsonRedisSerializer())); Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>(); ("myCache", config); return new FallbackRedisCacheManager(redisConnectionFactory, config, cacheConfigurations); } }
Implement FallbackRedisCacheManager: Create a custom oneCacheManager
, fallback to memory cache when Redis is unavailable.
import ; import ; import ; import ; import ; import ; import ; import ; 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(); ((new ConcurrentMapCache("fallbackCache"))); (); = simpleCacheManager; } @Override public Cache getCache(String name) { try { return (name); } catch (RedisConnectionFailureException e) { return (name); } } }
Use @Cacheable annotation: Used on methods that require cache@Cacheable
annotation.
import ; import ; @Service public class MyService { @Cacheable(value = "myCache", key = "#id") public String getData(String id) { // Simulate data acquisition process ("Fetching data from database for ID: " + id); return "Data for ID: " + id; } }
With the above configuration, when Redis is not available,FallbackRedisCacheManager
Will be capturedRedisConnectionFailureException
Exception and fallback to memory cache. This ensures that even if Redis goes down, the system will still run normally and return data. When using Spring Cache and Redis, if Redis has downtime or connection problems, you can configure it throughCacheManager
and implement customCacheErrorHandler
To ensure that the business logic can run normally even if the cache is unavailable. Here is a detailed solution example:
1. Add dependencies
First, make sure that you have added dependencies for Spring Boot Starter Cache and Spring Boot Starter Data Redis in your project:
<dependencies> <dependency> <groupId></groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId></groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> </dependencies>
2. Configure RedisTemplate
ConfigurationRedisTemplate
Storage objects using JSON serialization:
import ; import ; import ; import ; import .GenericJackson2JsonRedisSerializer; import ; @Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); (connectionFactory); (new StringRedisSerializer()); (new GenericJackson2JsonRedisSerializer()); (new StringRedisSerializer()); (new GenericJackson2JsonRedisSerializer()); return template; } }
3. Configure CacheManager
ConfigurationCacheManager
Use Redis as cache storage:
import ; import ; import ; import ; import ; import ; import ; import ; @Configuration @EnableCaching public class CacheConfig { @Bean public CacheManager cacheManager(RedisConnectionFactory connectionFactory) { RedisCacheConfiguration config = () .entryTtl((60)) // Set the cache expiration time to 60 minutes .disableCachingNullValues(); return (connectionFactory) .cacheDefaults(config) .build(); } }
4. Implement a custom CacheErrorHandler
Implement customCacheErrorHandler
, so that the cache operation fails:
import ; import ; import ; import ; @Configuration public class CacheConfig extends CachingConfigurerSupport { @Override public CacheErrorHandler errorHandler() { return new CacheErrorHandler() { @Override public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) { // Handle cache read errors ("Cache get error: " + ()); } @Override public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) { // Handle cache write errors ("Cache put error: " + ()); } @Override public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) { // Handle cache deletion error ("Cache evict error: " + ()); } @Override public void handleCacheClearError(RuntimeException exception, Cache cache) { // Handle cache clear error ("Cache clear error: " + ()); } }; } }
5. Use @Cacheable annotation
Use on methods that require cache@Cacheable
Note:
import ; import ; @Service public class UserService { @Cacheable(value = "users", key = "#userId") public User getUserById(String userId) { // Simulate to obtain user information from the database ("Fetching user from database: " + userId); return new User(userId, "John Doe"); } }
6. Test
You can test whether the above configuration is effective by simulating Redis downtime or disconnection. For example, you can temporarily close the Redis service and then callgetUserById
Method to check whether the data can be returned normally.
Summarize
Through the above configuration, when Redis is unavailable,CacheErrorHandler
Exception of cache operation will be caught and error messages will be printed. At the same time, since@Cacheable
The default behavior of annotations is to call the original method directly when the cache is unavailable, so the business logic will not be affected. This ensures high availability and stability of the system.
This is the article about the solution of Redis crashing when using @Cacheable annotation or other reasons that cannot be connected to the original method. This is all about this article. For more related @Cacheable annotations. Please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!