1. 程式人生 > >Springboot+SpringCache+Redis快取過期時間失效問題

Springboot+SpringCache+Redis快取過期時間失效問題

最近研究Spring+Redis快取時,發現Cacheable註解在方法體上標註了之後雖然能夠產生快取,但是在redis中的快取TIL是-1,介面返回的資料一直應用該快取,導致快取資料無法更新,網路上查詢發現大都是通過註解中配置方便每個方法自定義快取有效時間的方法。程式碼配置如下。

/**
 *重新配置RedisCacheManager
 * @param r
 */
@Autowired
public void configRedisCacheManger(RedisCacheManager r){
	r.setDefaultExpiration(100L);
	Map<String,Long> expires = Maps.newHashMap();
	expires.put("tGet",60L);
	// 設定快取的過期時間和自動重新整理時間
	r.setExpires(expires);
}
@Cacheable(cacheNames = tGet",key = "#xzqh",sync = true)
public Txxx get(String xzqh){
	......
}

檢視原始碼後發現在RedisCache中會對三種註解的方法分別執行不同的方法,而我使用的@CacheAble正好使用的get方法。原始碼貼上

public <T> T get(final Object key, final Callable<T> valueLoader) {
		//此處方法是應該完成過期時間增加,但是好像沒有
		BinaryRedisCacheElement rce = new BinaryRedisCacheElement(new RedisCacheElement(new RedisCacheKey(key).usePrefix(
				cacheMetadata.getKeyPrefix()).withKeySerializer(redisOperations.getKeySerializer()), valueLoader),
				cacheValueAccessor);

		ValueWrapper val = get(key);
		if (val != null) {
			return (T) val.get();
		}

		RedisWriteThroughCallback callback = new RedisWriteThroughCallback(rce, cacheMetadata);

		try {
			byte[] result = (byte[]) redisOperations.execute(callback);
			return (T) (result == null ? null : cacheValueAccessor.deserializeIfNecessary(result));
		} catch (RuntimeException e) {
			throw CacheValueRetrievalExceptionFactory.INSTANCE.create(key, valueLoader, e);
		}

	}

同樣的put方法。

	@Override
	public void put(final Object key, final Object value) {
		//然而這邊有的很明顯expireAfter()方法
		put(new RedisCacheElement(new RedisCacheKey(key).usePrefix(cacheMetadata.getKeyPrefix()).withKeySerializer(
				redisOperations.getKeySerializer()), value).expireAfter(cacheMetadata.getDefaultExpiration()));
	}

同樣測試@CachePut註解標註在介面上,介面資料正常快取,且帶預設的TIL值。

在RedisCache類中是使用內部類RedisWriteThroughCallBack的diInRedis()方法入庫的,其中設定過期時長的方法為:

		protected void processKeyExpiration(RedisCacheElement element, RedisConnection connection) {
			if (!element.isEternal()) {
				connection.expire(element.getKeyBytes(), element.getTimeToLive());
			}
		}

很明顯element中未定義TimeToLive值,因此快取在redis庫中的資料TIL都是-1,資料不會過期。

解決方法:原來的spring boot版本為1.4.4- spring-data-redis版本為1.7.7,更改redis版本為1.5.15,spring-data-redis版本自動切換為1.8.14。發現RedisCache中的get方法被修改為

	public <T> T get(final Object key, final Callable<T> valueLoader) {
		//添加了expireAfter選項
		RedisCacheElement cacheElement = new RedisCacheElement(getRedisCacheKey(key),
				new StoreTranslatingCallable(valueLoader)).expireAfter(cacheMetadata.getDefaultExpiration());
		BinaryRedisCacheElement rce = new BinaryRedisCacheElement(cacheElement, cacheValueAccessor);

		ValueWrapper val = get(key);
		if (val != null) {
			return (T) val.get();
		}

		RedisWriteThroughCallback callback = new RedisWriteThroughCallback(rce, cacheMetadata);

		try {
			byte[] result = (byte[]) redisOperations.execute(callback);
			return (T) (result == null ? null : fromStoreValue(cacheValueAccessor.deserializeIfNecessary(result)));
		} catch (RuntimeException e) {
			throw CacheValueRetrievalExceptionFactory.INSTANCE.create(key, valueLoader, e);
		}
	}

測試後發現快取能夠正常過期

PS:在springboot1.5以後,redis依賴中間添加了data,改為

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>