首页 > 编程语言 >用Java写一个分布式缓存——缓存管理

用Java写一个分布式缓存——缓存管理

时间:2023-01-27 14:45:08浏览次数:62  
标签:缓存 Java CacheObj Cache return new public 分布式

前言

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

标签:缓存,Java,CacheObj,Cache,return,new,public,分布式
From: https://www.cnblogs.com/weloe/p/17068891.html

相关文章

  • 【Java】用增强for循环遍历元素提示并发修改异常
    做二叉树遍历时使用了增强for循环,但是出现异常:Exceptioninthread"main"java.util.ConcurrentModificationException原因是:迭代器遍历元素的时候,通过集合是不能修改元素......
  • java23种设计模式
    Java设计模式学习创建型模式简单工厂模式简单工厂模式严格意义上来说,并不属于设计模式中的一种,不过这里还是简单记录下。定义:由一个工厂对象决定创建出哪一种类型实例......
  • Java集合
    一、什么是集合二、Collection体系集合1.Collection父接口2.List集合方法(list继承collection)publicstaticvoidmain(String[]args){//创建l......
  • java Enum 枚举
    简介java枚举是一个特殊的类,一般表示常量,比如一周7天,一年4个季节。示例enumLevelEnum{LOW,MEDIUM,HIGH}遍历publicclassEnumTest{publ......
  • LESSON FOUR:Java基础语法(上)
    Java基础语法注释单行注释://注释内容多行注释:/*注释内容*/文档注释:/**注释内容*//*.---..-----------*/\__/------*......
  • CSAPP随笔:高速缓存
       解决的问题:系统消耗大量的时间将信息从一个地方移动到另一个地方。较大的存储设备比较小的存储设备运行得慢。价格上速度快的要远高于速度慢的,同时加快处理......
  • 浪 - Java
    如果你的计算机还没有安装Java,这篇说明很适合你.OpenJDK下载地址历史版本归档环境配置cd~/.localwgethttps://download.java.net/java/ga/jdk11/openjdk-1......
  • Javase多态(对多态的理解、多态的体现)
    前言刚开始学Java的时候对多态这个概念一直没弄太明白,随着后面的学习无意中都在用着多态,又看了一些文章决定整理一下!举例创建一个Person类,Student类继承Person类,并分别创建......
  • 读Java8函数式编程笔记02_流
    1. 外部迭代1.1. for循环是一个封装了迭代的语法糖1.1.1. 本质上来讲是一种串行化操作1.2. 很难抽象出不同操作2. 内部迭代2.1. 内部迭代中的相应接口:Stream......
  • 【Javaweb】Servlet* | 请求重定向【
    请求重定向的含义请求重定向,是指客户端给服务器发请求,然后服务器告诉客户端说。我给你一些地址,你去新地址访问,叫请求重定向(因为之前的地址可能已经废弃)。  请求重定......