缓存系统的用来代替直接访问数据库,用来提升系统性能,减小数据库复杂。早期缓存跟系统在一个虚拟机里,这样内存访问,速度最快。 后来应用系统水平扩展,缓存作为一个独立系统存在,如redis,但是每次从缓存获取数据,都还是要通过网络访问才能获取,效率相对于早先从内存里获取,还是差了点。如果一个应用,比如传统的企业应用,一次页面显示,要访问数次redis,那效果就不是特别好,因此,现在有人提出了一二级缓存。即一级缓存跟系统在一个虚拟机内,这样速度最快。二级缓存位于redis里,当一级缓存没有数据的时候,再从redis里获取,并同步到一级缓存里。

现在实现这种一二级缓存的也挺多的,比如 hazelcast,新版的Ehcache..不过,实际上,如果你用spring boot,手里又一个Redis,则不需要搞hazelcastEhcache,只需要200行代码,就能在spring boot基础上,提供一个一二级缓存,代码如下:


import java.io.UnsupportedEncodingException;
import java.util.concurrent.ConcurrentHashMap;import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.data.redis.cache.RedisCache; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCachePrefix; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.connection.MessageListener; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.listener.PatternTopic; import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.data.redis.listener.adapter.MessageListenerAdapter; @Configuration @Conditional(StarterCacheCondition.class) public class CacheConfig { @Value("${springext.cache.redis.topic:cache}") String topicName ; @Bean public MyRedisCacheManager cacheManager(RedisTemplate<Object, Object> redisTemplate) { MyRedisCacheManager cacheManager = new MyRedisCacheManager(redisTemplate); cacheManager.setUsePrefix(true); return cacheManager; } @Bean RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) { RedisMessageListenerContainer container = new RedisMessageListenerContainer(); container.setConnectionFactory(connectionFactory); container.addMessageListener(listenerAdapter, new PatternTopic(topicName)); return container; } @Bean MessageListenerAdapter listenerAdapter(MyRedisCacheManager cacheManager ) { return new MessageListenerAdapter(new MessageListener(){ @Override public void onMessage(Message message, byte[] pattern) { byte[] bs = message.getChannel(); try { String type = new String(bs,"UTF-8"); cacheManager.receiver(type); } catch (UnsupportedEncodingException e) { e.printStackTrace(); // 不可能出错 } } }); } class MyRedisCacheManager extends RedisCacheManager{ public MyRedisCacheManager(RedisOperations redisOperations) { super(redisOperations); } @SuppressWarnings("unchecked") @Override protected RedisCache createCache(String cacheName) { long expiration = computeExpiration(cacheName); return new MyRedisCache(this,cacheName, (this.isUsePrefix()? this.getCachePrefix().prefix(cacheName) : null), this.getRedisOperations(), expiration); } /** * get a messsage for update cache * @param cacheName */ public void receiver(String cacheName){ MyRedisCache cache = (MyRedisCache)this.getCache(cacheName); if(cache==null){ return ; } cache.cacheUpdate(); } //notify other redis clent to update cache( clear local cache in fact) public void publishMessage(String cacheName){ this.getRedisOperations().convertAndSend(topicName, cacheName); } } class MyRedisCache extends RedisCache{ //local cache for performace ConcurrentHashMap<Object,ValueWrapper> local = new ConcurrentHashMap<>(); MyRedisCacheManager cacheManager; public MyRedisCache(MyRedisCacheManager cacheManager,String name, byte[] prefix, RedisOperations<? extends Object, ? extends Object> redisOperations, long expiration) { super(name, prefix, redisOperations, expiration); this.cacheManager = cacheManager; } @Override public ValueWrapper get(Object key) { ValueWrapper wrapper = local.get(key); if(wrapper!=null){ return wrapper; }else{ wrapper = super.get(key); if(wrapper!=null){ local.put(key, wrapper); } return wrapper; } } @Override public void put(final Object key, final Object value) { super.put(key, value); cacheManager.publishMessage(super.getName()); } @Override public void evict(Object key) { super.evict(key); cacheManager.publishMessage(super.getName()); } @Override public ValueWrapper putIfAbsent(Object key, final Object value){ ValueWrapper wrapper = super.putIfAbsent(key, value); cacheManager.publishMessage(super.getName()); return wrapper; } public void cacheUpdate(){ //clear all cache for simplification local.clear(); } } } class StarterCacheCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { RelaxedPropertyResolver resolver = new RelaxedPropertyResolver( context.getEnvironment(), "springext.cache."); String env = resolver.getProperty("type"); if(env==null){ return false; } return "local2redis".equalsIgnoreCase(env.toLowerCase()); } }

代码的核心在于spring boot提供一个概念CacheManager&Cache用来表示缓存,并提供了多达8种实现,但由于缺少一二级缓存,因此,需要在Redis基础上扩展,因此实现了MyRedisCacheManger,以及MyRedisCache,增加一个本地缓存。

一二级缓存需要解决的的一个问题是缓存更新的时候,必须通知其他节点的springboot应用缓存更新。这里可以用Redis的 Pub/Sub 功能来实现,具体可以参考listenerAdapter方法实现。

使用的时候,需要配置如下,这样,就可以使用缓存了,性能杠杠的好

springext.cache.type=local2redis# Redis服务器连接端口
spring.redis.host=172.16.86.56
spring.redis.port=6379 

转载于:https://www.cnblogs.com/telwanggs/p/10809557.html

SpringBoot,用200行代码完成一个一二级分布式缓存相关推荐

  1. 爬虫python代码-Python爬虫教程:200行代码实现一个滑动验证码

    Python爬虫教程:教你用200行代码实现一个滑动验证码 做网络爬虫的同学肯定见过各种各样的验证码,比较高级的有滑动.点选等样式,看起来好像挺复杂的,但实际上它们的核心原理还是还是很清晰的,本文章大 ...

  2. 前端 验证码隐藏怎么实现_Python爬虫教程:200行代码实现一个滑动验证码

    Python爬虫教程:教你用200行代码实现一个滑动验证码 做网络爬虫的同学肯定见过各种各样的验证码,比较高级的有滑动.点选等样式,看起来好像挺复杂的,但实际上它们的核心原理还是还是很清晰的,本文章大 ...

  3. python爬虫代码-Python爬虫教程:200行代码实现一个滑动验证码

    Python爬虫教程:教你用200行代码实现一个滑动验证码 做网络爬虫的同学肯定见过各种各样的验证码,比较高级的有滑动.点选等样式,看起来好像挺复杂的,但实际上它们的核心原理还是还是很清晰的,本文章大 ...

  4. 200行代码实现一个滑动验证码

    作者 | 崔庆才 转载自进击的Coder(ID: FightingCoder) 做网络爬虫的同学肯定见过各种各样的验证码,比较高级的有滑动.点选等样式,看起来好像挺复杂的,但实际上它们的核心原理还是还 ...

  5. float js 正则 验证_爬虫篇 | 200 行代码实现一个滑动验证码

    最近整理一个爬虫系列方面的文章,不管大家的基础如何,我从头开始整一个爬虫系列方面的文章,让大家循序渐进的学习爬虫,小白也没有学习障碍. 爬虫篇 | Python使用正则来爬取豆瓣图书数据 爬虫篇 | ...

  6. 200 行代码实现一个滑动验证码

    做网络爬虫的同学肯定见过各种各样的验证码,比较高级的有滑动.点选等样式,看起来好像挺复杂的,但实际上它们的核心原理还是还是很清晰的,本文章大致说明下这些验证码的原理以及带大家实现一个滑动验证码. 我之 ...

  7. 200行代码构建一个区块链

    区块链的基本概念非常简单:一个存储不断增加的有序记录的分布式数据库.然而,当我们谈论区块链时,我们很容易将其与区块链要解决的问题混淆,比如误解为流行的,基于区块链的,像比特币和以太坊一样的项目.术语& ...

  8. 200 行代码实现一个简单的区块链应用1

    区块链的基础概念很简单:一个分布式数据库,存储一个不断加长的 list,list 中包含着许多有序的记录.然而,在通常情况下,当我们谈到区块链的时候也会谈起使用区块链来解决的问题,这两者很容易混淆.像 ...

  9. 200 行代码实现一个简单的区块链

    英文原文:Lauri Hartikka 区块链的基础概念很简单:一个分布式数据库,存储一个不断加长的 list,list 中包含着许多有序的记录.然而,在通常情况下,当我们谈到区块链的时候也会谈起使用 ...

最新文章

  1. AI一分钟|阿里AI鉴黄师或将取代人类;特斯拉私有化空头潜在利润超10亿美元
  2. 我怎样才能参加比特币现金9月1日压力测试?
  3. java php mysql_系统学习javaweb13----MYSQL学习(使用PHP、SQL)1
  4. android view显示隐藏动画效果,Android 根据手势顶部View自动展示与隐藏效果
  5. HTML5 Canvas实现360度全景图
  6. python中import问题
  7. 55天 -算法 - poj4137百炼最小新整数 -贪心算法
  8. 浅谈EM算法的两个理解角度
  9. [Mac OS] Homebrew简介及安装wine
  10. css怎么写仿粗体,在CSS中字体加粗要怎么做?
  11. 电容屏物体识别_浅谈多点电容屏物体识别,实物识别技术
  12. oracle数据类型为文本类型,Oracle 字段类型
  13. 小红书API根据关键词取商品列表,Onebound数据
  14. java中如何添加画板背景图,几何画板中如何添加背景图片
  15. Python之OpenGL笔记(30):飘扬的旗帜
  16. 一个双向转换火星文的玩具
  17. Java最基础的算法逻辑题,新手一定要看看呀。
  18. 龙芯3a5000下编译redis 7.0源码
  19. kitti rotation,label等细节相关
  20. 直播系统---从直播答题看SEI帧的原理和作用

热门文章

  1. java 写oracle clob_Java将数据写入Oracle的Clob字段中
  2. (118)System Verilog 父类与子类对象复制(copy函数)详解
  3. (19)Zynq FPGA TTC定时器介绍
  4. matlab 多速率,多速率信号处理系统设计与实现.PDF
  5. 函数平移口诀_初三二次函数平移规律的口诀
  6. java内存泄漏跟栈溢出,8.《深入理解Java虚拟机》内存溢出和内存泄露、并行和并发、Minor GC和Full GC、Client模式和Server模式的区别...
  7. 数据结构之链式队列的优化
  8. MySQL C 语言应用程序接口开发教程
  9. libevent源码深度剖析四
  10. 三星uboot1.1.6源码分析——start.s(1)