前言
之前也用过一些缓存中间件,框架,也想着自己是不是也能用Java写一个出来,于是就有了这个想法,打算在写的过程中同步进行总结
源码:weloe/Java-Distributed-Cache (github.com)
本篇代码:
Java-Distributed-Cache/src/main/java/com/weloe/cache/cachemanager at master · weloe/Java-Distributed-Cache (github.com)
Java-Distributed-Cache/src/test/java/com/weloe/cache/cachemanager at master · weloe/Java-Distributed-Cache (github.com)
上篇:
https://www.cnblogs.com/weloe/p/17050512.html
思路
既然是分布式缓存,那么就一定会有缓存管理方面的问题,既然是要储存的数据,那么就不能让它无限制的存储,就需要设置临界值,这个也是需要缓存淘汰的原因。
而为了对缓存方便管理,比如,我们需要缓存的有多个功能,我们为了方便区分,可能就需要在key前加上功能前缀,这样不仅变得麻烦,同时由于key变大,也会增加内存的压力。
所以我们就需要把缓存分组进行管理,并提供一些方便的对外接口
实现CacheObj
Java-Distributed-Cache/CacheObj.java at master · weloe/Java-Distributed-Cache (github.com)
在前篇缓存淘汰中,我们确定了我们真正存储数据的是一个k,v结构,因此,我们需要抽象出这里的k,v,k选择String,而v则抽象出一个CacheObj。
需要注意的是,这里的endTime是该缓存到期的时间,一般而言,我们都有为目标缓存设定缓存时间的需求,这也是缓存淘汰策略中的一种。实际存储为byte[]则是为了通用性。
public class CacheObj { private LocalDateTime endTime; private Class clazz; private int byteSize; // 存储的实际数据 private byte[] data; public CacheObj() { } public CacheObj(LocalDateTime endTime,Class clazz ,int byteSize, byte[] data) { this.endTime = endTime; this.clazz = clazz; this.byteSize = byteSize; this.data = data; } public int getByteSize() { return byteSize; } public byte[] getData() { return data; } public void setEndTime(LocalDateTime endTime) { this.endTime = endTime; } public LocalDateTime getEndTime() { return endTime; } public void setClazz(Class clazz) { this.clazz = clazz; }}
Cache
Java-Distributed-Cache/Cache.java at master · weloe/Java-Distributed-Cache (github.com)
有组管理,也就需要单一的缓存管理
public class Cache { // 最大字节 private int maxByteSize; // 目前使用字节 private int normalByteSize; // 缓存策略 private CacheStrategy cacheStrategy; Lock readLock; Lock writeLock; public Cache(int maxByteSize, CacheStrategy cacheStrategy) { this.maxByteSize = maxByteSize; this.normalByteSize = 0; this.cacheStrategy = cacheStrategy; readLock = new ReentrantReadWriteLock().readLock(); writeLock = new ReentrantReadWriteLock().writeLock(); } public CacheObj add(String key, CacheObj cacheObj) { writeLock.lock(); normalByteSize += cacheObj.getByteSize(); // 缓存上限 while (normalByteSize > maxByteSize) { // 淘汰缓存 CacheObj outCache = cacheStrategy.outCache(); normalByteSize -= outCache.getByteSize(); } // 加入缓存 CacheObj v = cacheStrategy.put(key, cacheObj); writeLock.unlock(); return v; } public CacheObj get(String key) { readLock.lock(); CacheObj v = cacheStrategy.get(key); // 判断是否过期 if (v != null && v.getEndTime() != null && LocalDateTime.now().isAfter(v.getEndTime())) { CacheObj obj = cacheStrategy.outCache(key); return null; } readLock.unlock(); return v; } public CacheObj remove(String key){ return cacheStrategy.outCache(key); } public void clear(){ cacheStrategy.clear(); } public void setMaxByteSize(int maxByteSize) { this.maxByteSize = maxByteSize; } public int getMaxByteSize() { return maxByteSize; } public int getNormalByteSize() { return normalByteSize; }}
Group
Java-Distributed-Cache/Group.java at master · weloe/Java-Distributed-Cache (github.com)
既然需要组管理,那么就需要抽象出一个Group类型,这里的getter是需要后期自定义的回调函数。
public class Group { private String name; private Cache cache; private Getter getter; @FunctionalInterface interface Getter { byte[] get(String k) throws Exception; }
put,get
为了方便管理,Group需要提供get,put法
public CacheObj get(String key) { if ("".equals(key) || key == null) { throw new RuntimeException("key不能为空"); } CacheObj cacheObj = cache.get(key); if (cacheObj != null) { return cacheObj; } return load(key); } /** * 通过Getter回调获取数据 * * @param key * @return */ private CacheObj load(String key) { byte[] bytes = null; try { bytes = getter.get(key); } catch (Exception e) { e.printStackTrace(); return null; } if (bytes == null) { return null; } CacheObj cacheObj = BytesUtil.bytes2CacheObj(bytes); cache.add(key, cacheObj); return cacheObj; } public CacheObj putCacheObj(String key,CacheObj cacheObj){ CacheObj obj = cache.add(key, cacheObj); return obj; }
expire
为存储的数据设定存储时间的方法
public CacheObj expire(String key, long num, ChronoUnit timeUnit){ CacheObj cacheObj; try { cacheObj = cache.get(key); cacheObj.setEndTime(LocalDateTime.now().plus(num, timeUnit)); } catch (Exception e) { return null; } return cacheObj; }
setSize
设置缓存临界值的方法
public boolean setMaxSize(int num){ if(num < cache.getNormalByteSize()){ return false; } cache.setMaxByteSize(num); return true; }
delete,clear
清除组缓存的方法,从这里也可以看出其方便性,即可以清除单一功能(组)的缓存
public CacheObj delete(String key){ CacheObj obj = cache.remove(key); return obj; } public void clear(){ cache.clear(); }
GroupManager
Java-Distributed-Cache/GroupManager.java at master · weloe/Java-Distributed-Cache (github.com)
既然有Group,就需要管理Group,也就需要相对应的put,get方法
public class GroupManager { private Map groupMap; public GroupManager(Map groupMap) { this.groupMap = groupMap; } public Group getGroup(String key) { Group group = groupMap.get(key); return group; } public Group put(Group group){ return groupMap.put(group.getName(),group); }}
测试CacheTest
Java-Distributed-Cache/CacheTest.java at master · weloe/Java-Distributed-Cache (github.com)
class CacheTest { Cache cache; @BeforeEach void setUp() { CacheStrategy lruCache = new LRUCache(5); lruCache.setCallback((s1, s2)-> System.out.println("缓存淘汰")); cache = new Cache(1024*1024,lruCache); } @Test void add() { String s = "123"; CacheObj cacheObj = new CacheObj(LocalDateTime.MAX, String.class, 512*1024, s.getBytes(StandardCharsets.UTF_8)); cache.add("test", cacheObj); for (int i = 0; i < 5; i++) { cache.add("test"+i,cacheObj); } } @Test void get() { CacheObj cacheObj = cache.get("123"); Assertions.assertNull(cacheObj); String s = "123"; cacheObj = new CacheObj(LocalDateTime.MAX,String.class, s.getBytes(StandardCharsets.UTF_8).length, s.getBytes(StandardCharsets.UTF_8)); cache.add("test", cacheObj); CacheObj test = cache.get("test"); Assertions.assertNotNull(test); byte[] data = test.getData(); String s1 = BytesUtil.bytes2String(data); System.out.println(s1); }}
GroupTest
Java-Distributed-Cache/GroupTest.java at master · weloe/Java-Distributed-Cache (github.com)
package com.weloe.cache.cachemanager;import com.weloe.cache.outstrategy.CacheStrategy;import com.weloe.cache.outstrategy.LRUCache;import org.junit.jupiter.api.Assertions;import org.junit.jupiter.api.BeforeEach;import org.junit.jupiter.api.Test;import java.nio.charset.StandardCharsets;import java.time.temporal.ChronoUnit;import java.util.concurrent.TimeUnit;class GroupTest { Group group; @BeforeEach void setUp() { CacheStrategy lruCache = new LRUCache(5); lruCache.setCallback((s1, s2)-> System.out.println("缓存淘汰")); group = new Group("group1", new Cache(1024*1024,lruCache), str -> { System.out.println("group1回调"); return new byte[0]; }); } @Test void get() { group.putCacheObj("1",new CacheObj()); CacheObj cacheObj = group.get("1"); } @Test void getName() { String name = group.getName(); System.out.println(name); } @Test void putCacheObj() { String x = "132"; group.putCacheObj("cache1",new CacheObj(null,String.class,x.getBytes(StandardCharsets.UTF_8).length,x.getBytes(StandardCharsets.UTF_8))); } @Test void expire() { String x = "132"; group.putCacheObj("cache1",new CacheObj(null,String.class,x.getBytes(StandardCharsets.UTF_8).length,x.getBytes(StandardCharsets.UTF_8))); CacheObj cache1 = group.expire("cache1", 2, ChronoUnit.MINUTES); System.out.println(cache1.getEndTime()); System.out.println(group.get("cache1").getEndTime()); Assertions.assertSame(cache1.getEndTime(),group.get("cache1").getEndTime()); }}
GroupManagerTest
Java-Distributed-Cache/GroupManagerTest.java at master · weloe/Java-Distributed-Cache (github.com)
package com.weloe.cache.cachemanager;import com.weloe.cache.outstrategy.LRUCache;import org.junit.jupiter.api.BeforeEach;import org.junit.jupiter.api.Test;import java.util.HashMap;import java.util.concurrent.locks.ReentrantLock;class GroupManagerTest { GroupManager groupManager; @BeforeEach void setUp() { Group group1 = new Group("group1", new Cache(1024*1024, new LRUCache(5,(s1, s2)-> System.out.println("group1缓存淘汰"))), str -> {System.out.println("group1未获取缓存的回调");return new byte[0];} ); Group group2 = new Group("group2", new Cache(1024*1024, new LRUCache(5,(s1, s2)-> System.out.println("group2缓存淘汰"))), str -> {System.out.println("group2未获取缓存的回调");return new byte[0];} ); groupManager = new GroupManager(new HashMap(),new ReentrantLock()); groupManager.put(group1); groupManager.put(group2); } @Test void getGroup() { System.out.println(groupManager.getGroup("")); System.out.println(groupManager.getGroup("group1")); System.out.println(groupManager.getGroup("group2").getName()); } @Test void put() { Group group3 = new Group("group3", new Cache(1024*1024, new LRUCache(5,(s1, s2)-> System.out.println("group3缓存淘汰"))), str -> {System.out.println("group3未获取缓存的回调");return new byte[0];} ); groupManager.put(group3); System.out.println(groupManager.getGroup("group3").getName()); }}