之前也用過一些緩存中間件,框架,也想著自己是不是也能用Java寫一個出來,於是就有了這個想法,打算在寫的過程中同步進行總結 源碼:https://github.com/weloe/Java-Distributed-Cache ...
前言
之前也用過一些緩存中間件,框架,也想著自己是不是也能用Java寫一個出來,於是就有了這個想法,打算在寫的過程中同步進行總結
源碼: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<String, CacheObj> cacheStrategy;
Lock readLock;
Lock writeLock;
public Cache(int maxByteSize, CacheStrategy<String, CacheObj> 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<String, Group> groupMap;
public GroupManager(Map<String, Group> 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<String, CacheObj> 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<String, CacheObj> 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());
}
}