Skip to content

Commit

Permalink
Merge branch 'feature/20220824--支持更换redisTemplate' of github.com:xiao…
Browse files Browse the repository at this point in the history
…mingfor/multilevel-cache-spring-boot-starter into hot
  • Loading branch information
lbw committed Aug 24, 2022
2 parents 487f45f + e6076fa commit 6b7662a
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -28,10 +29,13 @@ public class MultilevelCacheAutoConfiguration {
@Bean
@ConditionalOnBean(RedisTemplate.class)
public RedisCaffeineCacheManager cacheManager(CacheConfigProperties cacheConfigProperties,
RedisTemplate<Object, Object> stringKeyRedisTemplate) {
@Qualifier("stringKeyRedisTemplate") RedisTemplate<Object, Object> stringKeyRedisTemplate) {
return new RedisCaffeineCacheManager(cacheConfigProperties, stringKeyRedisTemplate);
}

/**
* 可自定义名称为stringKeyRedisTemplate的RedisTemplate覆盖掉默认RedisTemplate。
*/
@Bean
@ConditionalOnMissingBean(name = "stringKeyRedisTemplate")
public RedisTemplate<Object, Object> stringKeyRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
Expand All @@ -44,11 +48,11 @@ public RedisTemplate<Object, Object> stringKeyRedisTemplate(RedisConnectionFacto

@Bean
public RedisMessageListenerContainer cacheMessageListenerContainer(CacheConfigProperties cacheConfigProperties,
RedisTemplate<Object, Object> stringKeyRedisTemplate, RedisCaffeineCacheManager redisCaffeineCacheManager) {
@Qualifier("stringKeyRedisTemplate") RedisTemplate<Object, Object> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -14,13 +14,17 @@
@RequiredArgsConstructor
public class CacheMessageListener implements MessageListener {

private final RedisTemplate<Object, Object> redisTemplate;
private RedisSerializer<Object> 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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -44,6 +46,10 @@ public class RedisCaffeineCache extends AbstractValueAdaptingCache {

private final Map<String, ReentrantLock> keyLockMap = new ConcurrentHashMap<>();

private RedisSerializer<String> stringSerializer = RedisSerializer.string();

private RedisSerializer<Object> javaSerializer = RedisSerializer.java();

public RedisCaffeineCache(String name, RedisTemplate<Object, Object> stringKeyRedisTemplate,
Cache<Object, Object> caffeineCache, CacheConfigProperties cacheConfigProperties) {
super(cacheConfigProperties.isCacheNullValues());
Expand Down Expand Up @@ -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);
}
Expand All @@ -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));

Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand All @@ -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);
}

/**
Expand All @@ -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;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
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();

}

0 comments on commit 6b7662a

Please sign in to comment.