三级缓存解决SpringBoot Redis Cache网络IO瓶颈

在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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值