在SpringBoot项目中使用 @Cache注解,使用 Redis Cache,发现网络流量高企,在不高的符合下,网卡接收流量高达90Mbps。
分析原因为 @Cache 注解的方法每次调用都会通过网络去Redis服务器中读取缓存数据,多次重复调用造成占用、浪费网络带宽。
既然如此,那就在本地做个缓存即可解决(需要占用一部分本机内存)
这里使用 Caffeine 缓存,缓存后网络流量将到500Kbps以下,效果非常明显。
Maven pom.xml 引入 com.github.ben-manes.caffeine
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.6.0</version>
</dependency>
写个@Configuration类配置
@EnableCaching
@Configuration
public class CacheConfig {
@Bean(name = "caffeineCacheManager")
CaffeineCacheManager caffeineCacheManager(
RedisTemplate<Object, Object> redisTemplate) {
MyCaffeineCacheManager cacheManager = new MyCaffeineCacheManager();
cacheManager.setCacheLoader(key -> {
Object value = redisTemplate.opsForValue().get(key);
System.out.println("load from redis by key:" + key + ", value:" + value);
return value;
});
Caffeine<Object, Object> caffeine = Caffeine.newBuilder();
caffeine.maximumSize(500);
caffeine.recordStats();
caffeine.writer(new CacheWriter<Object, Object>() {
@Override
public void write(Object key, Object value) {
System.out.println("write key:" + key + ", with value:" + value);
redisTemplate.opsForValue().set(key, value);
redisTemplate.convertAndSend("caffeineCacheEvent", key);
}
@Override
public void delete(Object key, Object value, RemovalCause cause) {
System.out.println("delete key:" + key + ", with value:" + value + ", by cause:" + cause);
if (cause == RemovalCause.SIZE || cause == RemovalCause.EXPIRED) {
} else {
redisTemplate.delete(key);
redisTemplate.convertAndSend("caffeineCacheEvent", key);
}
}
});
cacheManager.setCaffeine(caffeine);
MessageListenerAdapter adapter = new MessageListenerAdapter((MessageDelegate) (message, channel) -> {
System.out.println("Received message: " + message + ", at channel:" + channel);
String key = message.toString();
String split = MyCacheManager.CACHE_NAME_SPLIT_KEY;
Cache cache = cacheManager.getCache(key.substring(0, key.indexOf(split)));
System.out.println(cache.evictIfPresent(key.substring(key.indexOf(split) + split.length())));
});
adapter.afterPropertiesSet();
container.addMessageListener(adapter, ChannelTopic.of("caffeineCacheEvent"));
return cacheManager;
}
/**
* Pub Sub 监听 caffeineCacheEvent Channel
*/
@Bean
RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory, MyCaffeineCacheManager cacheManager) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
MessageListenerAdapter adapter = new MessageListenerAdapter((MessageDelegate) (message, channel) -> {
System.out.println("Received message: " + message + ", at channel:" + channel);
String key = message.toString();
String split = MyCacheManager.CACHE_NAME_SPLIT_KEY;
Cache cache = cacheManager.getCache(key.substring(0, key.indexOf(split)));
System.out.println(cache.evictIfPresent(key.substring(key.indexOf(split) + split.length())));
});
adapter.afterPropertiesSet();
container.addMessageListener(adapter, ChannelTopic.of("caffeineCacheEvent"));
return container;
}
}
public class MyCaffeineCacheManager extends CaffeineCacheManager {
public static String CACHE_NAME_SPLIT_KEY = "::";
@Override
protected Cache adaptCaffeineCache(String name, com.github.benmanes.caffeine.cache.Cache<Object, Object> cache) {
return new MyCaffeineCache(name, cache, isAllowNullValues());
}
}
public class MyCaffeineCache extends CaffeineCache {
public MyCaffeineCache(String name, Cache<Object, Object> cache, boolean allowNullValues) {
super(name, cache, allowNullValues);
}
@Nullable
@Override
public <T> T get(Object key, Callable<T> valueLoader) {
return super.get(makeKey(key), valueLoader);
}
@Nullable
@Override
protected Object lookup(Object key) {
return super.lookup(makeKey(key));
}
@Override
public void put(Object key, @Nullable Object value) {
super.put(makeKey(key), value);
}
@Nullable
@Override
public ValueWrapper putIfAbsent(Object key, @Nullable Object value) {
return super.putIfAbsent(makeKey(key), value);
}
@Override
public void evict(Object key) {
super.evict(makeKey(key));
}
@Override
public boolean evictIfPresent(Object key) {
return super.evictIfPresent(makeKey(key));
}
private Object makeKey(Object key) {
return getName() + MyCaffeineCacheManager.CACHE_NAME_SPLIT_KEY + key;
}
}
public interface MessageDelegate {
void handleMessage(Serializable message, String channel);
}
Caffeine缓存介绍 https://www.jianshu.com/p/9a80c662dac4