5.springboot-redis spring cache中篇

1.spring cache解析

1.1.RedisCache和RedisCacheManager

1.1.1.結構

RedisCacheManager結構

1.1.2.解析

  • RedisCache使用RedisCacheWriter接口,用於對redis的讀寫;
  • RedisCacheWriter
    • RedisCacheWriter和Cache接口的差異:
      • 所有方法都需要指定name,執行redis命令時如需加鎖,name爲鎖的key;
      • RedisCacheWriter可根據正則清除緩存;
public interface RedisCacheWriter {

	//**靜態方法,創建不帶鎖的RedisCacheWriter實例
	static RedisCacheWriter nonLockingRedisCacheWriter(RedisConnectionFactory connectionFactory) {
		Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
		return new DefaultRedisCacheWriter(connectionFactory);
	}

	//**靜態方法,創建帶鎖的RedisCacheWriter實例
	static RedisCacheWriter lockingRedisCacheWriter(RedisConnectionFactory connectionFactory) {
		Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
		return new DefaultRedisCacheWriter(connectionFactory, Duration.ofMillis(50));
	}

	//**保存鍵值對,可指定過期時間
	void put(String name, byte[] key, byte[] value, @Nullable Duration ttl);

	//**根據key獲取value
	byte[] get(String name, byte[] key);

	//**如果鍵值對不存在則保存,返回緩存中當前的value
	byte[] putIfAbsent(String name, byte[] key, byte[] value, @Nullable Duration ttl);

	//**刪除鍵值對
	void remove(String name, byte[] key);

	//**清除符合正則的緩存
	void clean(String name, byte[] pattern);
}
  • DefaultRedisCacheWriter
    • DefaultRedisCacheWriter是DefaultRedisCacheWriter接口唯一的實現類;
    • DefaultRedisCacheWriter是作用域爲包內可見,用戶無法創建,只能使用DefaultRedisCacheWriter接口提供的靜態方法;
    • 如果sleepTime爲Zero或者爲負數,則不會加鎖,否則在執行redis命令時會加鎖,默認爲Zero;
class DefaultRedisCacheWriter implements RedisCacheWriter {
    //**redis連接工廠
	private final RedisConnectionFactory connectionFactory;
	//**等待鎖,在有鎖時線程休眠sleepTime毫秒,然後重新獲取鎖。如果sleepTime爲Zero或者爲負數,則不會加鎖,否則在執行redis命令時會加鎖
	private final Duration sleepTime;

	//**設置鍵值對
	public void put(String name, byte[] key, byte[] value, @Nullable Duration ttl) {
		...
		//**獲取到鎖後執行
		execute(name, connection -> {
		     //**如果ttl有效,則保存鍵值對並設置有效期
			if (shouldExpireWithin(ttl)) {
				connection.set(key, value, Expiration.from(ttl.toMillis(), TimeUnit.MILLISECONDS), SetOption.upsert());
			} else {  //**否則,則保存鍵值對
				connection.set(key, value);
			}
			return "OK";
		});
	}

	//**如果鍵值對不存在則保存,返回緩存中當前的value
	public byte[] putIfAbsent(String name, byte[] key, byte[] value, @Nullable Duration ttl) {
        ...
        //**獲取到鎖後執行
		return execute(name, connection -> {
            //**如果需要加鎖,則加鎖
			if (isLockingCacheWriter()) {
				doLock(name, connection);
			}

			try {
			    //**鍵值對不存在時,設置鍵值對
				if (connection.setNX(key, value)) {
                    //**如果ttl有效,則設置有效期
					if (shouldExpireWithin(ttl)) {
						connection.pExpire(key, ttl.toMillis());
					}
					return null;
				}
                //**返回當前保存的值
				return connection.get(key);
			} finally {
                //**解鎖
				if (isLockingCacheWriter()) {
					doUnlock(name, connection);
				}
			}
		});
	}

	//**清除符合正則的緩存
	public void clean(String name, byte[] pattern) {
        ...
        //**獲取到鎖後執行
		execute(name, connection -> {
            //**是否加鎖成功
			boolean wasLocked = false;
			try {
                //**如果需要加鎖,則加鎖,並設置加鎖成功
				if (isLockingCacheWriter()) {
					doLock(name, connection);
					wasLocked = true;
				}
                //**根據正則獲取key
				byte[][] keys = Optional.ofNullable(connection.keys(pattern)).orElse(Collections.emptySet())
						.toArray(new byte[0][]);
                //**如果有配置的key,則全部刪除
				if (keys.length > 0) {
					connection.del(keys);
				}
			} finally {
                //**如果加鎖成功並且需要加鎖,則解鎖
				if (wasLocked && isLockingCacheWriter()) {
					doUnlock(name, connection);
				}
			}

			return "OK";
		});
	}

	//**加鎖
	void lock(String name) {
		execute(name, connection -> doLock(name, connection));
	}

	//**解鎖
	void unlock(String name) {
		executeLockFree(connection -> doUnlock(name, connection));
	}

    //**加鎖
	private Boolean doLock(String name, RedisConnection connection) {
		return connection.setNX(createCacheLockKey(name), new byte[0]);
	}

    //**解鎖
	private Long doUnlock(String name, RedisConnection connection) {
		return connection.del(createCacheLockKey(name));
	}

    //**檢查是否加鎖
	boolean doCheckLock(String name, RedisConnection connection) {
		return connection.exists(createCacheLockKey(name));
	}

	//**是否加鎖
	private boolean isLockingCacheWriter() {
		return !sleepTime.isZero() && !sleepTime.isNegative();
	}

    //**執行redis命令(沒有獲取到鎖時會一直等待,直到獲取鎖,然後執行redis命令)
	private <T> T execute(String name, Function<RedisConnection, T> callback) {
		RedisConnection connection = connectionFactory.getConnection();
		try {

			checkAndPotentiallyWaitUntilUnlocked(name, connection);
			return callback.apply(connection);
		} finally {
			connection.close();
		}
	}

    //**執行redis命令
	private void executeLockFree(Consumer<RedisConnection> callback) {
		RedisConnection connection = connectionFactory.getConnection();
		try {
			callback.accept(connection);
		} finally {
			connection.close();
		}
	}
    
    //**等待鎖(有鎖時線程休眠sleepTime毫秒,然後重新獲取鎖)
	private void checkAndPotentiallyWaitUntilUnlocked(String name, RedisConnection connection) {
		if (!isLockingCacheWriter()) {
			return;
		}
		try {
			while (doCheckLock(name, connection)) {
				Thread.sleep(sleepTime.toMillis());
			}
		} catch (InterruptedException ex) {
			// Re-interrupt current thread, to allow other participants to react.
			Thread.currentThread().interrupt();
			throw new PessimisticLockingFailureException(String.format("Interrupted while waiting to unlock cache %s", name),
					ex);
		}
	}

    //**ttl是否有有效
	private static boolean shouldExpireWithin(@Nullable Duration ttl) {
		return ttl != null && !ttl.isZero() && !ttl.isNegative();
	}

    //**根據name創建鎖的key
	private static byte[] createCacheLockKey(String name) {
		return (name + "~lock").getBytes(StandardCharsets.UTF_8);
	}
}
  • RedisCacheConfiguration
    • RedisCache可通過RedisCacheConfiguration配置常用選項;
    • RedisCacheConfiguration使用了建造者模式,由於RedisCacheConfiguration各項屬性都爲final,所以先初始化所有屬性創建一個默認的對象,然後在每個初始化屬性的方法中都是重新創建一個對象;
      • 創建示例:RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(60));
public class RedisCacheConfiguration {
    //**緩存有效期
	private final Duration ttl;
	//**是否緩存空值
	private final boolean cacheNullValues;
	//**key前綴計算器
	private final CacheKeyPrefix keyPrefix;
	//**是否使用前綴
	private final boolean usePrefix;
	
    //**key的序列化器,持有RedisSerializer的引用,key只能是string
	private final SerializationPair<String> keySerializationPair;
	//**value的序列化器,持有RedisSerializer的引用,value可以是任意類型
	private final SerializationPair<Object> valueSerializationPair;
    
    //**spring ConversionService類型轉換
	private final ConversionService conversionService;

	//**靜態方法,返回默認的RedisCacheConfiguration實例
	public static RedisCacheConfiguration defaultCacheConfig() {
		return defaultCacheConfig(null);
	}

	//**靜態方法,返回默認的RedisCacheConfiguration實例
	//**創建一個無過期時間,允許緩存空值,key需使用前綴且前綴爲"name::"格式,key序列化器爲StringRedisSerializer,
	//**value序列化器這jdk序列化器,conversionService爲DefaultFormattingConversionService的RedisCacheConfiguration
	public static RedisCacheConfiguration defaultCacheConfig(@Nullable ClassLoader classLoader) {
		DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
		registerDefaultConverters(conversionService);
		return new RedisCacheConfiguration(Duration.ZERO, true, true, CacheKeyPrefix.simple(),
				SerializationPair.fromSerializer(RedisSerializer.string()),
				SerializationPair.fromSerializer(RedisSerializer.java(classLoader)), conversionService);
	}

	//**創建一個RedisCacheConfiguration,並設置緩存過期時間
	public RedisCacheConfiguration entryTtl(Duration ttl) {
		Assert.notNull(ttl, "TTL duration must not be null!");
		return new RedisCacheConfiguration(ttl, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
				valueSerializationPair, conversionService);
	}
    
    ...一系列的建造者模式初始化屬性的方法
	
	//**根據cacheName生成key的前綴
	public String getKeyPrefixFor(String cacheName) {
		Assert.notNull(cacheName, "Cache name must not be null!");
		return keyPrefix.compute(cacheName);
	}

	//**conversionService中添加Converter
	public void addCacheKeyConverter(Converter<?, String> cacheKeyConverter) {
		configureKeyConverters(it -> it.addConverter(cacheKeyConverter));
	}

	//**conversionService配置
	public void configureKeyConverters(Consumer<ConverterRegistry> registryConsumer) {
		...
		registryConsumer.accept((ConverterRegistry) getConversionService());
	}

	//**註冊兩個Converter
	public static void registerDefaultConverters(ConverterRegistry registry) {
		Assert.notNull(registry, "ConverterRegistry must not be null!");
		registry.addConverter(String.class, byte[].class, source -> source.getBytes(StandardCharsets.UTF_8));
		registry.addConverter(SimpleKey.class, String.class, SimpleKey::toString);
	}
}
  • RedisCache
    • 使用RedisCacheWriter操作redis讀寫;
    • 使用cacheConfig的keySerializationPair和valueSerializationPair對key和value進行序列化和反序列化;
public class RedisCache extends AbstractValueAdaptingCache {
    //**使用jdk序列化器序列化NullValue
	private static final byte[] BINARY_NULL_VALUE = RedisSerializer.java().serialize(NullValue.INSTANCE);
    //**cache的name
	private final String name;
	//**RedisCacheWriter實例
	private final RedisCacheWriter cacheWriter;
	//**redis配置
	private final RedisCacheConfiguration cacheConfig;
	//**spring ConversionService類型轉換
	private final ConversionService conversionService;

    //**獲取value,返回object對象
	protected Object lookup(Object key) {
	    //**使用cacheWriter獲取value
		byte[] value = cacheWriter.get(name, createAndConvertCacheKey(key));
		if (value == null) {
			return null;
		}
		
		//**cacheConfig的value序列化器反序列化value
		return deserializeCacheValue(value);
	}

    //**獲取value,返回object對象,如果value不存在,則使用valueLoader生成value
	public synchronized <T> T get(Object key, Callable<T> valueLoader) {
        //**調用lookup方法,獲取value,並轉換爲ValueWrapper對象
		ValueWrapper result = get(key);
        //**如果value不爲空,則返回value,並強制轉換爲指定類型
		if (result != null) {
			return (T) result.get();
		}
        //**如果value爲空,則把valueLoader的結果做爲value保存,並返回
		T value = valueFromLoader(key, valueLoader);
		put(key, value);
		return value;
	}

	//**保存鍵值對
	public void put(Object key, @Nullable Object value) {
        //**對value進行轉換,如果value爲null並且允許空值,則轉換爲NullValue
		Object cacheValue = preProcessCacheValue(value);
        //**如果value爲null並且不允許空值,則拋出異常
		if (!isAllowNullValues() && cacheValue == null) {

			throw new IllegalArgumentException(String.format(
					"Cache '%s' does not allow 'null' values. Avoid storing null via '@Cacheable(unless=\"#result == null\")' or configure RedisCache to allow 'null' via RedisCacheConfiguration.",
					name));
		}
        //**使用cacheConfig的value序列化器序列化value,然後使用cacheWriter保存鍵值對,並設置過期時間
		cacheWriter.put(name, createAndConvertCacheKey(key), serializeCacheValue(cacheValue), cacheConfig.getTtl());
	}

	//**刪除鍵值對
	public void evict(Object key) {
		cacheWriter.remove(name, createAndConvertCacheKey(key));
	}

	//**清除所有鍵值對
	public void clear() {
		byte[] pattern = conversionService.convert(createCacheKey("*"), byte[].class);
		cacheWriter.clean(name, pattern);
	}

	//**對value進行轉換,如果value爲null並且允許空值,則轉換爲NullValue
	protected Object preProcessCacheValue(@Nullable Object value) {
		if (value != null) {
			return value;
		}
		return isAllowNullValues() ? NullValue.INSTANCE : null;
	}

	//**使用cacheConfig的key序列化器序列化key
	protected byte[] serializeCacheKey(String cacheKey) {
		return ByteUtils.getBytes(cacheConfig.getKeySerializationPair().write(cacheKey));
	}

	//**使用cacheConfig的value序列化器序列化value
	protected byte[] serializeCacheValue(Object value) {
		if (isAllowNullValues() && value instanceof NullValue) {
			return BINARY_NULL_VALUE;
		}
		return ByteUtils.getBytes(cacheConfig.getValueSerializationPair().write(value));
	}

	//**使用cacheConfig的value序列化器反序列化value
	protected Object deserializeCacheValue(byte[] value) {
		if (isAllowNullValues() && ObjectUtils.nullSafeEquals(value, BINARY_NULL_VALUE)) {
			return NullValue.INSTANCE;
		}
		return cacheConfig.getValueSerializationPair().read(ByteBuffer.wrap(value));
	}

	//**根據指定的key,生成保存在redis中的key
	protected String createCacheKey(Object key) {
	    //**把key轉換爲string
		String convertedKey = convertKey(key);
		if (!cacheConfig.usePrefix()) {
			return convertedKey;
		}
		//**如果key需要前綴,則使用cacheConfig的keyPrefix計算器計算key值,默認"name::key"
		return prefixCacheKey(convertedKey);
	}

	//**把key轉換爲string
	protected String convertKey(Object key) {
        //**如果key爲string類型,則直接返回
		if (key instanceof String) {
			return (String) key;
		}
        
		TypeDescriptor source = TypeDescriptor.forObject(key);
        //**如果key能轉換爲string,則轉換爲string,並返回
		if (conversionService.canConvert(source, TypeDescriptor.valueOf(String.class))) {
			...
			return conversionService.convert(key, String.class);
			...
		}

        //**調用key的toString方法轉換爲string,並返回
		Method toString = ReflectionUtils.findMethod(key.getClass(), "toString");
		if (toString != null && !Object.class.equals(toString.getDeclaringClass())) {
			return key.toString();
		}
        ...
	}
}
  • RedisCacheManager
public class RedisCacheManager extends AbstractTransactionSupportingCacheManager {
    //**redis讀寫
	private final RedisCacheWriter cacheWriter;
	//**redis配置
	private final RedisCacheConfiguration defaultCacheConfig;
	//**初始化緩存配置
	private final Map<String, RedisCacheConfiguration> initialCacheConfiguration;
	//**name對應的cache不存在時,是否允許創建新的cache
	private final boolean allowInFlightCacheCreation;

	//**靜態方法,根據connectionFactory創建默認的RedisCacheManager
	public static RedisCacheManager create(RedisConnectionFactory connectionFactory) {
		Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
		return new RedisCacheManager(new DefaultRedisCacheWriter(connectionFactory),
				RedisCacheConfiguration.defaultCacheConfig());
	}

	//**建造者模式,根據connectionFactory創建RedisCacheManagerBuilder
	public static RedisCacheManagerBuilder builder(RedisConnectionFactory connectionFactory) {
		Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
		return RedisCacheManagerBuilder.fromConnectionFactory(connectionFactory);
	}

    //**建造者模式,根據cacheWriter創建RedisCacheManagerBuilder
	public static RedisCacheManagerBuilder builder(RedisCacheWriter cacheWriter) {
		Assert.notNull(cacheWriter, "CacheWriter must not be null!");
		return RedisCacheManagerBuilder.fromCacheWriter(cacheWriter);
	}

	//**根據初始化緩存配置加載cache
	protected Collection<RedisCache> loadCaches() {
		List<RedisCache> caches = new LinkedList<>();
		for (Map.Entry<String, RedisCacheConfiguration> entry : initialCacheConfiguration.entrySet()) {
			caches.add(createRedisCache(entry.getKey(), entry.getValue()));
		}
		return caches;
	}

	//**如果name對應的cache不存在時,並且允許創建緩存,則根據defaultCacheConfig創建新的cache
	protected RedisCache getMissingCache(String name) {
		return allowInFlightCacheCreation ? createRedisCache(name, defaultCacheConfig) : null;
	}

    //**根據name創建RedisCache
	protected RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) {
		return new RedisCache(name, cacheWriter, cacheConfig != null ? cacheConfig : defaultCacheConfig);
	}

	//**建造者
	public static class RedisCacheManagerBuilder {

		private final RedisCacheWriter cacheWriter;
		//**創建默認的RedisCacheConfiguration
		private RedisCacheConfiguration defaultCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
		//**默認不初始化cache
		private final Map<String, RedisCacheConfiguration> initialCaches = new LinkedHashMap<>();
		//**是否啓用事務
		private boolean enableTransactions;
		//**name對應的cache不存在時,默認允許允許創建新的cache
		boolean allowInFlightCacheCreation = true;
	}
}

1.1.3.RedisCacheManager配置示例

  • RedisCacheConfiguration
    • key序列化器爲StringRedisSerializer;
    • value序列化器爲GenericJackson2JsonRedisSerializer;
    • 緩存過期時間爲60秒;
  • DefaultRedisCacheWriter
    • 執行redis命令時不會加鎖;
  • RedisCache
    • 允許空值;
  • RedisCacheManager
    • 沒有初始化cache;
    • 根據name獲取cache,cache不存在時創建cache;
    • 不支持事務;
@Bean
@ConditionalOnMissingBean(name = "cacheManager")
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
    //**redis默認配置文件,並且設置過期時間爲60秒
    RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(60));
    //**設置序列化器
    redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json()));
    //**創建RedisCacheManager生成器
    RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(redisCacheConfiguration);
    return builder.build();
}

1.2.CompositeCacheManager

  • 組合其它的cacheManager,可同時使用多種緩存;
  • 問題:默認情況下getCache方法,當name對應的cache不存在時,會創建一個cache並返回。導致遍歷的第一個cacheManager永遠會返回cache,後面的cache就永遠不會調用到,所以CompositeCacheManager管理的cacheManager必須初始化cache並禁止動態創建cache?;
public class CompositeCacheManager implements CacheManager, InitializingBean {
    //**管理的其它CacheManager
	private final List<CacheManager> cacheManagers = new ArrayList<>();
    //**是否在末尾添加一個NoOpCacheManager
	private boolean fallbackToNoOpCache = false;

    //**在末尾添加一個NoOpCacheManager,調用getCache匹配不到CacheManager的都會返回NoOpCacheManager
	public void afterPropertiesSet() {
		if (this.fallbackToNoOpCache) {
			this.cacheManagers.add(new NoOpCacheManager());
		}
	}

    //**根據name獲取cache(問題:默認情況下getCache方法,當name對應的cache不存在時,會創建一個cache並返回
    //**導致遍歷的第一個cacheManager永遠會返回cache,後面的cache就永遠不會調用到)
	public Cache getCache(String name) {
		for (CacheManager cacheManager : this.cacheManagers) {
			Cache cache = cacheManager.getCache(name);
			if (cache != null) {
				return cache;
			}
		}
		return null;
	}

	//**獲取所有的name
	public Collection<String> getCacheNames() {
		Set<String> names = new LinkedHashSet<>();
		for (CacheManager manager : this.cacheManagers) {
			names.addAll(manager.getCacheNames());
		}
		return Collections.unmodifiableSet(names);
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章