From 3450f74b67a448cfc9f855b157ca5f585b2e1dc4 Mon Sep 17 00:00:00 2001 From: xiaomingfor Date: Wed, 24 Aug 2022 13:53:41 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feature=EF=BC=9A=E6=94=AF=E6=8C=81=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89redis=E5=BA=8F=E5=88=97=E5=8C=96=E6=96=B9?= =?UTF-8?q?=E5=BC=8F=EF=BC=8C=E5=8E=9F=E5=85=88=E7=9A=84jdk=E5=BA=8F?= =?UTF-8?q?=E5=88=97=E5=8C=96=E6=96=B9=E5=BC=8F=E4=B8=8D=E5=8F=97=E5=BD=B1?= =?UTF-8?q?=E5=93=8D=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MultilevelCacheAutoConfiguration.java | 11 ++-- .../cache/support/CacheMessageListener.java | 10 ++- .../cache/support/RedisCaffeineCache.java | 65 ++++++++++++++----- .../plugin/cache/support/RedisNullValue.java | 13 ++++ 4 files changed, 77 insertions(+), 22 deletions(-) create mode 100644 src/main/java/com/pig4cloud/plugin/cache/support/RedisNullValue.java diff --git a/src/main/java/com/pig4cloud/plugin/cache/MultilevelCacheAutoConfiguration.java b/src/main/java/com/pig4cloud/plugin/cache/MultilevelCacheAutoConfiguration.java index 5704006..8793496 100755 --- a/src/main/java/com/pig4cloud/plugin/cache/MultilevelCacheAutoConfiguration.java +++ b/src/main/java/com/pig4cloud/plugin/cache/MultilevelCacheAutoConfiguration.java @@ -3,6 +3,7 @@ import com.pig4cloud.plugin.cache.properties.CacheConfigProperties; import com.pig4cloud.plugin.cache.support.CacheMessageListener; import com.pig4cloud.plugin.cache.support.RedisCaffeineCacheManager; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -28,10 +29,13 @@ public class MultilevelCacheAutoConfiguration { @Bean @ConditionalOnBean(RedisTemplate.class) public RedisCaffeineCacheManager cacheManager(CacheConfigProperties cacheConfigProperties, - RedisTemplate stringKeyRedisTemplate) { + @Qualifier("stringKeyRedisTemplate")RedisTemplate stringKeyRedisTemplate) { return new RedisCaffeineCacheManager(cacheConfigProperties, stringKeyRedisTemplate); } + /** + * 可自定义名称为stringKeyRedisTemplate的RedisTemplate覆盖掉默认RedisTemplate。 + */ @Bean @ConditionalOnMissingBean(name = "stringKeyRedisTemplate") public RedisTemplate stringKeyRedisTemplate(RedisConnectionFactory redisConnectionFactory) { @@ -44,11 +48,10 @@ public RedisTemplate stringKeyRedisTemplate(RedisConnectionFacto @Bean public RedisMessageListenerContainer cacheMessageListenerContainer(CacheConfigProperties cacheConfigProperties, - RedisTemplate stringKeyRedisTemplate, RedisCaffeineCacheManager redisCaffeineCacheManager) { + @Qualifier("stringKeyRedisTemplate") RedisTemplate stringKeyRedisTemplate, RedisCaffeineCacheManager redisCaffeineCacheManager) { RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer(); redisMessageListenerContainer.setConnectionFactory(stringKeyRedisTemplate.getConnectionFactory()); - CacheMessageListener cacheMessageListener = new CacheMessageListener(stringKeyRedisTemplate, - redisCaffeineCacheManager); + CacheMessageListener cacheMessageListener = new CacheMessageListener(redisCaffeineCacheManager); redisMessageListenerContainer.addMessageListener(cacheMessageListener, new ChannelTopic(cacheConfigProperties.getRedis().getTopic())); return redisMessageListenerContainer; diff --git a/src/main/java/com/pig4cloud/plugin/cache/support/CacheMessageListener.java b/src/main/java/com/pig4cloud/plugin/cache/support/CacheMessageListener.java index 6b98dff..1c07d70 100755 --- a/src/main/java/com/pig4cloud/plugin/cache/support/CacheMessageListener.java +++ b/src/main/java/com/pig4cloud/plugin/cache/support/CacheMessageListener.java @@ -4,7 +4,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.connection.MessageListener; -import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.RedisSerializer; /** * @author fuwei.deng @@ -14,13 +14,17 @@ @RequiredArgsConstructor public class CacheMessageListener implements MessageListener { - private final RedisTemplate redisTemplate; + private RedisSerializer javaSerializer = RedisSerializer.java(); private final RedisCaffeineCacheManager redisCaffeineCacheManager; @Override public void onMessage(Message message, byte[] pattern) { - CacheMessage cacheMessage = (CacheMessage) redisTemplate.getValueSerializer().deserialize(message.getBody()); + + /** + * 发送端固定了jdk序列户方式,接收端同样固定了jdk序列化方式进行反序列化。 + */ + CacheMessage cacheMessage = (CacheMessage) javaSerializer.deserialize(message.getBody()); log.debug("recevice a redis topic message, clear local cache, the cacheName is {}, the key is {}", cacheMessage.getCacheName(), cacheMessage.getKey()); redisCaffeineCacheManager.clearLocal(cacheMessage.getCacheName(), cacheMessage.getKey()); diff --git a/src/main/java/com/pig4cloud/plugin/cache/support/RedisCaffeineCache.java b/src/main/java/com/pig4cloud/plugin/cache/support/RedisCaffeineCache.java index fc00716..3011680 100755 --- a/src/main/java/com/pig4cloud/plugin/cache/support/RedisCaffeineCache.java +++ b/src/main/java/com/pig4cloud/plugin/cache/support/RedisCaffeineCache.java @@ -5,8 +5,10 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.cache.support.AbstractValueAdaptingCache; +import org.springframework.cache.support.NullValue; import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.serializer.StringRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; @@ -44,6 +46,10 @@ public class RedisCaffeineCache extends AbstractValueAdaptingCache { private final Map keyLockMap = new ConcurrentHashMap<>(); + private RedisSerializer stringSerializer = RedisSerializer.string(); + + private RedisSerializer javaSerializer = RedisSerializer.java(); + public RedisCaffeineCache(String name, RedisTemplate stringKeyRedisTemplate, Cache caffeineCache, CacheConfigProperties cacheConfigProperties) { super(cacheConfigProperties.isCacheNullValues()); @@ -105,11 +111,10 @@ public void put(Object key, Object value) { @Override public ValueWrapper putIfAbsent(Object key, Object value) { - Object cacheKey = getKey(key); Object prevValue; // 考虑使用分布式锁,或者将redis的setIfAbsent改为原子性操作 synchronized (key) { - prevValue = stringKeyRedisTemplate.opsForValue().get(cacheKey); + prevValue = getRedisValue(key); if (prevValue == null) { doPut(key, value); } @@ -118,14 +123,9 @@ public ValueWrapper putIfAbsent(Object key, Object value) { } private void doPut(Object key, Object value) { - Duration expire = getExpire(value); value = toStoreValue(value); - if (!expire.isNegative() && !expire.isZero()) { - stringKeyRedisTemplate.opsForValue().set(getKey(key), value, expire); - } - else { - stringKeyRedisTemplate.opsForValue().set(getKey(key), value); - } + Duration expire = getExpire(value); + setRedisValue(key, value, expire); push(new CacheMessage(this.name, key)); @@ -165,9 +165,7 @@ protected Object lookup(Object key) { return value; } - // 避免自动一个 RedisTemplate 覆盖失效 - stringKeyRedisTemplate.setKeySerializer(new StringRedisSerializer()); - value = stringKeyRedisTemplate.opsForValue().get(cacheKey); + value = getRedisValue(key); if (value != null) { log.debug("get cache from redis and put in caffeine, the key is : {}", cacheKey); @@ -186,7 +184,7 @@ private Duration getExpire(Object value) { if (cacheNameExpire == null) { cacheNameExpire = defaultExpiration; } - if (value == null && this.defaultNullValuesExpiration != null) { + if ((value == null || value == NullValue.INSTANCE) && this.defaultNullValuesExpiration != null) { cacheNameExpire = this.defaultNullValuesExpiration; } return cacheNameExpire; @@ -200,7 +198,19 @@ private Duration getExpire(Object value) { * @version 1.0.0 */ private void push(CacheMessage message) { - stringKeyRedisTemplate.convertAndSend(topic, message); + + /** + * 为了能自定义redisTemplate,发布订阅的序列化方式固定为jdk序列化方式。 + */ + Assert.hasText(topic, "a non-empty channel is required"); + byte[] rawChannel = stringSerializer.serialize(topic); + byte[] rawMessage = javaSerializer.serialize(message); + stringKeyRedisTemplate.execute((connection) -> { + connection.publish(rawChannel, rawMessage); + return null; + }, true); + + //stringKeyRedisTemplate.convertAndSend(topic, message); } /** @@ -220,4 +230,29 @@ public void clearLocal(Object key) { } } + private void setRedisValue(Object key, Object value, Duration expire) { + + Object convertValue = value; + if (value == null || value == NullValue.INSTANCE) { + convertValue = RedisNullValue.REDISNULLVALUE; + } + + if (!expire.isNegative() && !expire.isZero()) { + stringKeyRedisTemplate.opsForValue().set(getKey(key), convertValue, expire); + } + else { + stringKeyRedisTemplate.opsForValue().set(getKey(key), convertValue); + } + } + + private Object getRedisValue(Object key) { + + // NullValue在不同序列化方式中存在问题,因此自定义了RedisNullValue做个转化。 + Object value = stringKeyRedisTemplate.opsForValue().get(getKey(key)); + if (value != null && value instanceof RedisNullValue) { + value = NullValue.INSTANCE; + } + return value; + } + } diff --git a/src/main/java/com/pig4cloud/plugin/cache/support/RedisNullValue.java b/src/main/java/com/pig4cloud/plugin/cache/support/RedisNullValue.java new file mode 100644 index 0000000..3b88797 --- /dev/null +++ b/src/main/java/com/pig4cloud/plugin/cache/support/RedisNullValue.java @@ -0,0 +1,13 @@ +package com.pig4cloud.plugin.cache.support; + +import lombok.Data; + +import java.io.Serializable; + +@Data +class RedisNullValue implements Serializable { + + private static final long serialVersionUID = 1L; + + public static RedisNullValue REDISNULLVALUE = new RedisNullValue(); +} From e6076fabbf02e8a3a496348afac0aa67269644fa Mon Sep 17 00:00:00 2001 From: xiaomingfor Date: Wed, 24 Aug 2022 14:02:27 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix=EF=BC=9A=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/cache/MultilevelCacheAutoConfiguration.java | 7 ++++--- .../pig4cloud/plugin/cache/support/RedisCaffeineCache.java | 2 +- .../com/pig4cloud/plugin/cache/support/RedisNullValue.java | 5 +++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/pig4cloud/plugin/cache/MultilevelCacheAutoConfiguration.java b/src/main/java/com/pig4cloud/plugin/cache/MultilevelCacheAutoConfiguration.java index 8793496..221be8d 100755 --- a/src/main/java/com/pig4cloud/plugin/cache/MultilevelCacheAutoConfiguration.java +++ b/src/main/java/com/pig4cloud/plugin/cache/MultilevelCacheAutoConfiguration.java @@ -29,12 +29,12 @@ public class MultilevelCacheAutoConfiguration { @Bean @ConditionalOnBean(RedisTemplate.class) public RedisCaffeineCacheManager cacheManager(CacheConfigProperties cacheConfigProperties, - @Qualifier("stringKeyRedisTemplate")RedisTemplate stringKeyRedisTemplate) { + @Qualifier("stringKeyRedisTemplate") RedisTemplate stringKeyRedisTemplate) { return new RedisCaffeineCacheManager(cacheConfigProperties, stringKeyRedisTemplate); } /** - * 可自定义名称为stringKeyRedisTemplate的RedisTemplate覆盖掉默认RedisTemplate。 + * 可自定义名称为stringKeyRedisTemplate的RedisTemplate覆盖掉默认RedisTemplate。 */ @Bean @ConditionalOnMissingBean(name = "stringKeyRedisTemplate") @@ -48,7 +48,8 @@ public RedisTemplate stringKeyRedisTemplate(RedisConnectionFacto @Bean public RedisMessageListenerContainer cacheMessageListenerContainer(CacheConfigProperties cacheConfigProperties, - @Qualifier("stringKeyRedisTemplate") RedisTemplate stringKeyRedisTemplate, RedisCaffeineCacheManager redisCaffeineCacheManager) { + @Qualifier("stringKeyRedisTemplate") RedisTemplate stringKeyRedisTemplate, + RedisCaffeineCacheManager redisCaffeineCacheManager) { RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer(); redisMessageListenerContainer.setConnectionFactory(stringKeyRedisTemplate.getConnectionFactory()); CacheMessageListener cacheMessageListener = new CacheMessageListener(redisCaffeineCacheManager); diff --git a/src/main/java/com/pig4cloud/plugin/cache/support/RedisCaffeineCache.java b/src/main/java/com/pig4cloud/plugin/cache/support/RedisCaffeineCache.java index 3011680..1b1fb5b 100755 --- a/src/main/java/com/pig4cloud/plugin/cache/support/RedisCaffeineCache.java +++ b/src/main/java/com/pig4cloud/plugin/cache/support/RedisCaffeineCache.java @@ -210,7 +210,7 @@ private void push(CacheMessage message) { return null; }, true); - //stringKeyRedisTemplate.convertAndSend(topic, message); + // stringKeyRedisTemplate.convertAndSend(topic, message); } /** diff --git a/src/main/java/com/pig4cloud/plugin/cache/support/RedisNullValue.java b/src/main/java/com/pig4cloud/plugin/cache/support/RedisNullValue.java index 3b88797..a20de75 100644 --- a/src/main/java/com/pig4cloud/plugin/cache/support/RedisNullValue.java +++ b/src/main/java/com/pig4cloud/plugin/cache/support/RedisNullValue.java @@ -7,7 +7,8 @@ @Data class RedisNullValue implements Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; + + public static RedisNullValue REDISNULLVALUE = new RedisNullValue(); - public static RedisNullValue REDISNULLVALUE = new RedisNullValue(); }