前言

之前也用过一些缓存中间件,框架,也想着自己是不是也能用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写一个分布式缓存——缓存管理的更多相关文章

  1. 五:用JAVA写一个阿里云VPC Open API调用程序

    用JAVA写一个阿里云VPC Open API调用程序 摘要:用JAVA拼出来Open API的URL 引言 VPC提供了丰富的API接口,让网络工程是可以通过API调用的方式管理网络资源.用程序和软 ...

  2. 用JAVA写一个函数,功能例如以下: 随意给定一组数, 找出随意数相加之后的结果为35(随意设定)的情况

    用JAVA写一个函数.功能例如以下:随意给定一组数,比如{12,60,-8,99,15,35,17,18},找出随意数相加之后的结果为35(随意设定)的情况. 能够递归算法来解: package te ...

  3. 用java写一个servlet,可以将放在tomcat项目根目录下的文件进行下载

    用java写一个servlet,可以将放在tomcat项目根目录下的文件进行下载,将一个完整的项目进行展示,主要有以下几个部分: 1.servlet部分   Export 2.工具类:TxtFileU ...

  4. 用JAVA写一个多线程程序,写四个线程,其中二个对一个变量加1,另外二个对一个变量减1

    package com.ljn.base; /** * @author lijinnan * @date:2013-9-12 上午9:55:32 */ public class IncDecThrea ...

  5. 使用JAVA写一个简单的日历

    JAVA写一个简单的日历import java.text.DateFormat;import java.text.ParseException;import java.text.SimpleDateF ...

  6. Java实现一个简单的缓存方法

    缓存是在web开发中经常用到的,将程序经常使用到或调用到的对象存在内存中,或者是耗时较长但又不具有实时性的查询数据放入内存中,在一定程度上可以提高性能和效率.下面我实现了一个简单的缓存,步骤如下. 创 ...

  7. 《用Java写一个通用的服务器程序》01 综述

    最近一两年用C++写了好几个基于TCP通信类型程序,都是写一个小型的服务器,监听请求,解析自定义的协议,处理请求,返回结果.每次写新程序时都把老代码拿来,修改一下协议解析部分和业务处理部分,然后就一个 ...

  8. java 写一个"HelloJavaWorld你好世界"输出到操作系统文件Hello.txt文件中

    package com.beiwo.homework; import java.io.File; import java.io.FileOutputStream; import java.io.IOE ...

  9. Java写一个简单学生管理系统

    其实作为一名Java的程序猿,无论你是初学也好,大神也罢,学生管理系统一直都是一个非常好的例子,初学者主要是用数组.List等等来写出一个简易的学生管理系统,二.牛逼一点的大神则用数据库+swing来 ...

  10. 用java写一个用户登陆界面

    一.课堂测试源代码及其结果截图 用java的swing写一个用户登录界面,采用网格布局.源代码如下: /** * */package LiuLijia; import java.awt.CardLay ...

随机推荐

  1. 一篇文章带你了解轻量级Web服务器——Nginx简单入门

    一篇文章带你了解轻量级Web服务器--Nginx简单入门 Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件代理服务器 在本篇中我们会简单介绍Nginx的特点,安装,相关指令使用以及配置信 ...

  2. 嵌入式-C语言基础:字符串strlen和sizeof的区别

    strlen表示的实际的字符串长度,不会把字符串结束符'\0'计算进去,而sizeof则不是实际的字符串长度,它会把字符串的结束标识符'\0'也包含进去. #include<stdio.h> ...

  3. 第三方模块的下载与使用、requests模块、爬取链家二手房数据、openpyxl模块、hashlib加密模块

    目录 第三方模块的下载与使用 下载第三方模块可能会出现的问题 网络爬虫模块之requests模块 网络爬虫实战之爬取链家二手房数据 自动化办公领域之openpyxl模块 第三方模块的下载与使用 第三方 ...

  4. Kafka事务原理剖析

    一.事务概览 提起事务,我们第一印象可能就是ACID,需要满足原子性.一致性.事务隔离级别等概念,那kafka的事务能做到什么程度呢?我们首先看一下如何使用事务 Producer端代码如下 Kafka ...

  5. CopyOnWriteArrayList 是如何保证线程安全的?

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 前言 大家好,我是小彭. 在上一篇文章里,我们聊到了ArrayList 的线程安全问题,其中提到了 Copy ...

  6. 微信小程序根据开发环境切换域名

     domain.js // 获取当前账号信息,线上小程序版本号仅支持在正式版小程序中获取,开发版和体验版中无法获取. // envVersion:'develop','trial','release' ...

  7. 【第7篇】AI语音交互原理介绍

    本章主要介绍AI语音交互的原理,包括语音交互的流程以及各流程节点所涉及的相关知识,如语音采集.语音识别.自然语言处理.语音合成等. 2.1 AI语音交互 AI语音交互通俗点说就是人与机器间进行语音理解 ...

  8. @responseBody 返回更多数据

    @responseBody:注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML数据,需 ...

  9. jquery 简单分页插件jQuerypage

    昨天项目手机端要用到table的分页,考虑到手机端界面小,系统数据不多,在没考虑大批量数据处理的前提前就下载了这个插件,简单. 展示数据datas为json格式. <!DOCTYPE html& ...

  10. 【SQL真题】SQL1: 各个视频的平均完播率 【AVG/SUM/IF/CASE】

    题目: https://www.nowcoder.com/practice/96263162f69a48df9d84a93c71045753?tpId=268&tqId=2285032& ...