用Java寫一個分散式緩存——緩存管理

来源:https://www.cnblogs.com/weloe/archive/2023/01/27/17068891.html
-Advertisement-
Play Games

之前也用過一些緩存中間件,框架,也想著自己是不是也能用Java寫一個出來,於是就有了這個想法,打算在寫的過程中同步進行總結 源碼:https://github.com/weloe/Java-Distributed-Cache ...


前言

之前也用過一些緩存中間件,框架,也想著自己是不是也能用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());
    }
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 閉包和作用域 變數聲明 var 聲明特點 在使用var聲明變數時,變數會被自動添加到最接近的上下文 var存在聲明提升。var聲明會被拿到函數或全局作用域的頂部,位於作用域中所有代碼之前。 可多次重覆聲明。而重覆的var聲明則會被忽略 let 聲明特點 let聲明存在塊級作用域 let聲明(創建過程 ...
  • 如果你在項目中使用了 vuex模塊化,並且在項目中使用actions中函數調用頻率高,推薦瞭解一下這種方式。 比如下麵兩種方式調用 , 第一個是直接傳參設置, 第二個是添加了非同步ajax返回內容 在回調到等下我們要封裝的js中的成功回調里,然後這個成功回調就會反饋給組件 1.創建文件utils/vu ...
  • 一、Lua應用場景 游戲開發 獨立應用腳本 Web 應用腳本 擴展和資料庫插件如:MySQL Proxy 和 MySQL WorkBench 安全系統,如入侵檢測系統 教程採用Aide Lua Pro或AndLua+開發安卓應用。在學習開發安卓應用前,先學習lua的基礎課程。 二、配置手機開發環境 ...
  • 實現Spring底層機制-01 主要實現:初始化IOC容器+依賴註入+BeanPostProcessor機制+AOP 前面我們實際上已經使用代碼簡單實現了: Spring XML 註入 bean (Spring基本介紹02) Spring 註解方式註入 bean (Spring管理Bean-IOC- ...
  • 簡介: 訪問者模式,屬於行為型的設計模式。表示一個作用於某對象結構中的各元素的操作。它是你可以在不改變各元素的類的前提下定義作用於這些元素的新操作。 適用場景: 類中有易於變化的演算法。 希望數據結構與數據分離。 優點: 便於增加新的操作,相當於增加一個訪問者。訪問者模式將有關行為集中到一個訪問者對象 ...
  • 1.簡介 1.1 gRPC的起源 RPC是Remote Procedure Call的簡稱,中文叫遠程過程調用。用於解決分散式系統中服務之間的調用問題。通俗地講,就是開發者能夠像調用本地方法一樣調用遠程的服務。所以,RPC的作用主要體現在這兩個方面: 屏蔽遠程調用跟本地調用的區別,讓我們感覺就是調用 ...
  • 寫在開頭:本文章提供深搜與寬搜的解題思路,無具體題目對應的代碼,如想瞭解,請到個人主頁查找,感謝觀看。 深度優先搜索(DFS): 遞歸,即函數調用自身,以逐步減小問題 的規模。但在一些問題中,並不是所有的 遞歸路徑都是有效的。 如圖所示迷宮,很可能會進入橙色所標識 的“死衚衕”,只能回到之前的路徑, ...
  • 題目描述 牛牛嘗試用鍵盤讀入一個字元,然後在屏幕上顯示用這個字元組成的 3*3 的矩形。 輸入描述 一行讀入一個 char 類型的字元。 輸出描述 輸出這個字元組成的 3*3 矩形。 示例 1 輸入:# 輸出: ### ### ### 解題思路 方案一 採用多條 printf() 按照格式輸出,從而 ...
一周排行
    -Advertisement-
    Play Games
  • 在C#中使用SQL Server實現事務的ACID(原子性、一致性、隔離性、持久性)屬性和使用資料庫鎖(悲觀鎖和樂觀鎖)時,你可以通過ADO.NET的SqlConnection和SqlTransaction類來實現。下麵是一些示例和概念說明。 實現ACID事務 ACID屬性是事務處理的四個基本特征, ...
  • 我們在《SqlSugar開發框架》中,Winform界面開發部分往往也用到了自定義的用戶控制項,對應一些特殊的界面或者常用到的一些局部界面內容,我們可以使用自定義的用戶控制項來提高界面的統一性,同時也增強了使用的便利性。如我們Winform界面中用到的分頁控制項、附件顯示內容、以及一些公司、部門、菜單的下... ...
  • 在本篇教程中,我們學習瞭如何在 Taurus.MVC WebMVC 中進行數據綁定操作。我們還學習瞭如何使用 ${屬性名稱} CMS 語法來綁定頁面上的元素與 Model 中的屬性。通過這些步驟,我們成功實現了一個簡單的數據綁定示例。 ...
  • 是在MVVM中用來傳遞消息的一種方式。它是在MVVMLight框架中提供的一個實現了IMessenger介面的類,可以用來在ViewModel之間、ViewModel和View之間傳遞消息。 Send 接受一個泛型參數,表示要發送的消息內容。 Register 方法用於註冊某個對象接收消息。 pub ...
  • 概述:在WPF中,通過EventHandler可實現基礎和高級的UI更新方式。基礎用法涉及在類中定義事件,併在UI中訂閱以執行更新操作。高級用法藉助Dispatcher類,確保在非UI線程上執行操作後,通過UI線程更新界面。這兩種方法提供了靈活而可靠的UI更新機制。 在WPF(Windows Pre ...
  • 概述:本文介紹了在C#程式開發中如何利用自定義擴展方法測量代碼執行時間。通過使用簡單的Action委托,開發者可以輕鬆獲取代碼塊的執行時間,幫助優化性能、驗證演算法效率以及監控系統性能。這種通用方法提供了一種便捷而有效的方式,有助於提高開發效率和代碼質量。 在軟體開發中,瞭解代碼執行時間是優化程式性能 ...
  • 概述:Cron表達式是一種強大的定時任務調度工具,通過配置不同欄位實現靈活的時間規定。在.NET中,Quartz庫提供了簡便的方式配置Cron表達式,實現精準的定時任務調度。這種靈活性和可擴展性使得開發者能夠根據需求輕鬆地制定和管理定時任務,例如每天備份系統日誌或其他重要操作。 Cron表達式詳解 ...
  • 概述:.NET提供多種定時器,如System.Windows.Forms.Timer適用於UI,System.Web.UI.Timer用於Web,System.Diagnostics.Timer用於性能監控,System.Threading.Timer和System.Timers.Timer用於一般 ...
  • 問題背景 有同事聯繫我說,在生產環境上,訪問不了我負責的common服務,然後我去檢查common服務的health endpoint, 沒問題,然後我問了下異常,timeout導致的System.OperationCanceledException。那大概率是客戶端的問題,會不會是埠耗盡,用ne ...
  • 前言: 在本篇 Taurus.MVC WebMVC 入門開發教程的第四篇文章中, 我們將學習如何實現數據列表的綁定,通過使用 List<Model> 來展示多個數據項。 我們將繼續使用 Taurus.Mvc 命名空間,同時探討如何在視圖中綁定並顯示一個 Model 列表。 步驟1:創建 Model ...