前言

之前也用过一些缓存中间件,框架,也想着自己是不是也能用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());    }}