概述
缓存框架我们有ehcache 和 redis 分别是 本地内存缓存和 分布式缓存框架。在实际情况下如果单台机器 使用ehcache 就可以满足需求了,速度快效率高,有些数据如果需要多台机器共享这个时候怎么办呢,我们需要通过redis,将缓存存放到redis上面。
这也会导致一个问题,因为所有的请求都会到redis读取,当大量的读取会导致大量的网络流量,因此网络流量会成为访问的瓶颈。
J2CACHE就是解决这个问题而生的,缓存分为两级
L1: 进程内缓存(caffeine\ehcache)
L2: Redis/Memcached 集中式缓存
数据读取流程
读取顺序 -> L1 -> L2
缓存先读取L1 ,不存在则读取L2
数据更新
1 从数据库中读取最新数据,依次更新 L1 -> L2 ,发送广播清除某个缓存信息
2 接收到广播(手工清除缓存 & 一级缓存自动失效),从 L1 中清除指定的缓存信息
测试J2CACHE
创建一个maven 项目
引入
<dependency><!-- Ehcache 3.x //--> <groupId>org.ehcache</groupId> <artifactId>ehcache</artifactId> <version>3.4.0</version> </dependency> <dependency> <groupId>net.oschina.j2cache</groupId> <artifactId>j2cache-core</artifactId> <version>2.7.7-release</version> </dependency>
引入配置文件
ehcache3.xml
j2cache.properties
这个配置文件配置如下:
#J2Cache configuration ######################################### # Cache Broadcast Method # values: # jgroups -> use jgroups's multicast # redis -> use redis publish/subscribe mechanism (using jedis) # lettuce -> use redis publish/subscribe mechanism (using lettuce, Recommend) # rabbitmq -> use RabbitMQ publisher/consumer mechanism # rocketmq -> use RocketMQ publisher/consumer mechanism # none -> don't notify the other nodes in cluster # xx.xxxx.xxxx.Xxxxx your own cache broadcast policy classname that implement net.oschina.j2cache.cluster.ClusterPolicy ######################################### j2cache.broadcast = redis ######################################### # Level 1&2 provider # values: # none -> disable this level cache # ehcache -> use ehcache2 as level 1 cache # ehcache3 -> use ehcache3 as level 1 cache # caffeine -> use caffeine as level 1 cache(only in memory) # redis -> use redis as level 2 cache (using jedis) # lettuce -> use redis as level 2 cache (using lettuce) # readonly-redis -> use redis as level 2 cache ,but never write data to it. if use this provider, you must uncomment `j2cache.L2.config_section` to make the redis configurations available. # memcached -> use memcached as level 2 cache (xmemcached), # [classname] -> use custom provider ######################################### j2cache.L1.provider_class = ehcache3 j2cache.L2.provider_class = redis # When L2 provider isn't `redis`, using `L2.config_section = redis` to read redis configurations # j2cache.L2.config_section = redis # Enable/Disable ttl in redis cache data (if disabled, the object in redis will never expire, default:true) # NOTICE: redis hash mode (redis.storage = hash) do not support this feature) j2cache.sync_ttl_to_redis = true # Whether to cache null objects by default (default false) j2cache.default_cache_null_object = true ######################################### # Cache Serialization Provider # values: # fst -> using fast-serialization (recommend) # kyro -> using kyro serialization # json -> using fst's json serialization (testing) # fastjson -> using fastjson serialization (embed non-static class not support) # java -> java standard # [classname implements Serializer] ######################################### j2cache.serialization = fst #json.map.person = net.oschina.j2cache.demo.Person ######################################### # Ehcache configuration ######################################### # ehcache.configXml = /ehcache.xml # ehcache3.configXml = /ehcache3.xml # ehcache3.defaultHeapSize = 1000 ######################################### # Redis connection configuration ######################################### ######################################### # Redis Cluster Mode # # single -> single redis server # sentinel -> master-slaves servers # cluster -> cluster servers (\u93c1\u7248\u5d41\u6434\u64bb\u53a4\u7f03\ue1bd\u68e4\u93c1\u582c\u7d1d\u6d63\u8de8\u6564 database = 0\u951b\ufffd # sharded -> sharded servers (\u7035\u55d9\u721c\u9286\u4f79\u669f\u93b9\ue1bc\u7c31\u8e47\u5474\u300f\u9366\ufffd hosts \u6d93\ue15f\u5bda\u7039\u6c3e\u7d1d\u6d93\u65c7\u7e5b\u93ba\u30e6\u775c\u95b0\u5d87\u7586\u93c3\u72b3\u6665 ; redis://user:password@127.0.0.1:6379/0\u951b\ufffd # ######################################### redis.mode = sentinel #redis storage mode (generic|hash) redis.storage = generic ## redis pub/sub channel name redis.channel = j2cache ## redis pub/sub server (using redis.hosts when empty) redis.channel.host = #cluster name just for sharded redis.cluster_name = mymaster ## redis cache namespace optional, default[empty] redis.namespace = ## connection # Separate multiple redis nodes with commas, such as 192.168.0.10:6379,192.168.0.11:6379,192.168.0.12:6379 redis.hosts = 202.10.79.170:16001,202.10.79.170:16002,202.10.79.170:16003 redis.timeout = 10000 redis.password = redis.database = 0 ## redis pool properties redis.maxTotal = 100 redis.maxIdle = 10 redis.maxWaitMillis = 5000 redis.minEvictableIdleTimeMillis = 60000 redis.minIdle = 1 redis.numTestsPerEvictionRun = 10 redis.lifo = false redis.softMinEvictableIdleTimeMillis = 10 redis.testOnBorrow = true redis.testOnReturn = false redis.testWhileIdle = true redis.timeBetweenEvictionRunsMillis = 300000 redis.blockWhenExhausted = false redis.jmxEnabled = false
广播方式使用redis
j2cache.broadcast = redis
L1,L2 缓存实现方法
j2cache.L1.provider_class = ehcache3 j2cache.L2.provider_class = redis
redis 使用哨兵模式进行配置。
使用代码进行测试
编写测试代码如下:
读取缓存
public static void main(String[] args) throws InterruptedException { CacheChannel cache = J2Cache.getChannel(); while(true){ System.out.println(cache.get("user", "name")); Thread.sleep(2000); } }
这个代码会一直读取 user:name 的缓存。
我们需要测试的是,当一个客户端设置一个 user:name 的缓存时,这个代码能反映缓存的变化。
设置缓存:
public static void main(String[] args) { CacheChannel cache = J2Cache.getChannel(); cache.set("user", "name", "A"); cache.close(); }
首先设置一个A的缓存。
我们可以看到第一个代码返回的是如下情况
[user,name,L2]=>A
[user,name,L1]=>A
[user,name,L1]=>A
它会先从 redis读取,在读取本地。
我们在设置一次:
public static void main(String[] args) { CacheChannel cache = J2Cache.getChannel(); cache.set("user", "name", "B"); cache.close(); }
第二次我们这个name 为 B,这个时候我们可以发现:
[user,name,L2]=>B
[user,name,L1]=>B
[user,name,L1]=>B
缓存变成了B ,而且是先读取 REDIS ,在读取 EHCACHE缓存,即先读取 L2 再读取L1。
这样我们的缓存就既能保证性能,有可以保证缓存及时被更新。