自定义缓存框架-基于java的Map结构,内存缓存。

一、缓存介绍

本部分,缓存实现了如下功能
           缓存置入 缓存获取 缓存过期 缓存情空 缓存覆盖(实现用户自定义和默认配置)

二、缓存知识点梳理

缓存类型 概述
边缘缓存 https://www.jianshu.com/p/73034ee83f39,https://blog.csdn.net/weixin_43700280/article/details/85009717
页面缓存 动态语言生成的静态代码比如,php,jsp等
数据库缓存 https://www.cnblogs.com/hadley/p/9557596.html 预留缓存Cache-aside,Read-through,write-through
应用缓存 不管是本地内存还是磁盘,其速度快,成本低,在有些场合非常有效;分布式缓存这种东西存在的目的就是为了提供比RDB更高的TPS和扩展性,同时有帮你承担了数据同步的痛苦;优秀的分布式缓存系统有大家所熟知的Memcached、Redis(当然也许你把它看成是NoSQL,但是我个人更愿意把分布式缓存也看成是NoSQL),还有国内阿里自主开发的Tair等;
平台级缓存框架 https://blog.csdn.net/frankenjoy123/article/details/57952492;https://blog.csdn.net/d12345678a/article/details/77837039;https://blog.csdn.net/zjttlance/article/details/80234341
Web代理 http://www.people.com.cn/GB/channel5/569/20000731/166044.html
应用级缓存 http://www.importnew.com/21570.html;

nginx本地缓存,抗的是热数据的高并发访问。我们以电商为例,一般来讲,商品的购买总是有热点的,比如访问某些知名品牌的次数会比小众品牌次数多。这些热数据,由于经常被访问,我们可以利用nginx本地缓存。由于nginx本地内存有限,我们只cache住部分热数据,其余不怎么热的数据,流量可以走redis。

redis大规模分布式缓存集群,抗的是很高的离散访问,支撑海量的数据,高并发的访问,提供高可用的服务。

tomcat 的jvm堆内存缓存,主要是抗redis大规模灾难(比如雪崩),如果redis出现了大规模的宕机,导致nginx大量流量直接涌入数据生产服务,那么最后的tomcat堆内存缓存至少可以再抗一下,不至于让数据库直接裸奔。同时tomcat jvm堆内存缓存,也可以抗住redis没有cache住的最后那少量的部分缓存。

分布式缓存可采用Redis、MemCache等来实现,在没有Redis之前,如何保证集群共用一份缓存呢,在我理解,单独设计缓存集群服务器,所有的访问都可通过RPC机制去获取数据是否存在,带来的问题是高频读取,读压力的问题,可以通过负载均衡等手段实现。

在本部分所做的缓存设计属于单服务器缓存,主要用来管理和减轻由java生成的代码级缓存的读操作压力,应属于jvm堆内存部分。

缓存预热、缓存穿透、缓存雪崩,缓存更新以后梳理。本部分只介绍框架设计。

三、设计思路

核心:接口设计-可配置设计-多并发机制处理设计

基础接口设计

自定义缓存框架-基于java的Map结构,内存缓存。_第1张图片

put 用于置入缓存对象、get用于获取缓存对象、Remove用于移除缓存对象、RemoveAll用于将缓存清空。

自定义缓存框架-基于java的Map结构,内存缓存。_第2张图片

配置接口,setExpiredTime设置过期时间、isCover设置是否覆盖,true覆盖同key值缓存对象,false不覆盖同key值缓存对象。

为什么需要设计是否覆盖这个问题,由于在一定时间内,重复key的数据如果value不一致,会导致缓存的结果查询不一致。

主要应用场景,缓存优先。先产生的数据起效场景,当然这种可以用队列来做,FIFO。针对短期有效数据通过缓存来设计,比如2分钟内抢蛋糕时间,有10个蛋糕100个人抢,假如A蛋糕20个去预定,那么只有第一个起效。

三、核心实现

package com.hecore.cache.core;

import com.hecore.cache.iface.IHecoreCache;
import com.hecore.cache.pojo.CacheConf;
import com.hecore.cache.pojo.CacheObj;

import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;

/**
 * IHecoreCache 接口的默认实现,可用于单实例(注意多线程将会造成数据的不正确).
 * 1.构造类实现定时清理功能,生命周期。
 */
public class HecoreCache implements IHecoreCache{

    /**
     * 安全的并发包
     */
    private ConcurrentHashMap map=new ConcurrentHashMap<>();
    private int interval= 90 * 1000;
    private Timer timer;
    private CacheConf conf=null;

    public HecoreCache(CacheConf conf){
        this.conf=conf;
        CacheObj.DEFAULT_EXPIRE_TIME=conf.setExpiredTime(conf);
        timerClearMap();
    }

    private void timerClearMap() {
        timer = new Timer("HecoreCache", true);
        timer.schedule(
                new TimerTask() {
                    public void run() {
                        for (Map.Entry e : map.entrySet()) {
                            if (e.getValue().isExpired()) {
                                map.remove(e.getKey());
                            }
                        }
                    }
                },
                interval,
                interval
        );
    }

    /**
     * 防止覆盖,保证数据的一致性
     * @param cacheObj
     */
    @Override
    public void put(CacheObj cacheObj) {
        if(conf.isCover(conf)){
            map.put(cacheObj.getKey(),cacheObj);
        }else{//不覆盖
            if(!map.containsKey(cacheObj.getKey())){
                map.put(cacheObj.getKey(),cacheObj);
            }
        }
    }

    /**
     * @param key 为null 不应当存在于map中,属于无效值
     * @return
     */
    @Override
    public CacheObj get(String key) {
        return key!=null?map.get(key):null;
    }

    @Override
    public void remove(String key) {
        map.remove(key);
    }

    @Override
    public void removeAll() {
        map.clear();
    }

    /**
     * 防止再次加载造成数据重复
     * @param key
     * @return
     */
    public boolean contains(String key) {
        return map.containsKey(key);
    }
}
package com.hecore.cache.manager;

import com.hecore.cache.core.HecoreCache;
import com.hecore.cache.exception.HecoreExceotion;
import com.hecore.cache.iface.IHecoreCache;
import com.hecore.cache.pojo.CacheConf;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 用于多实例并发处理,保证只有唯一一个HecoreCache.
 */
public class HecoreCacheManager {

    private static HecoreCacheManager me=new HecoreCacheManager();

    // 类锁,如果作为静态变量就变成了类实例锁。那么每个线程进入都会等待全局的lock释放锁.,每时每刻只有一个线程会获取到。
    private Lock lock=new ReentrantLock();

    public HecoreCacheManager() {
        if(conf==null){
            lock.lock();
            conf=new CacheConf(360,true);
            lock.unlock();
        }
    }

    public HecoreCacheManager(CacheConf conf) {
        this.conf = conf;
        this.conf=conf;
    }

    private CacheConf conf=null;

    // 解决内存可见性问题,在什么场景下出现
    private volatile IHecoreCache iHecoreCache=null; // 设置为volatile 并发过程中线程可见。

    private void setHecoreCache(IHecoreCache iHecoreCache){
        if (iHecoreCache == null)
            throw new HecoreExceotion("null exception");
        this.iHecoreCache=iHecoreCache;
    }

    public IHecoreCache getiHecoreCache(){

        if (iHecoreCache == null){
            lock.lock();
            if (iHecoreCache==null)
                iHecoreCache=new HecoreCache(conf);
            lock.unlock();
        }
        return iHecoreCache;
    }

}

四、代码测试

        HecoreCacheManager hcm=new HecoreCacheManager(new CacheConf(180,false));
        IHecoreCache iHecoreCache=hcm.getiHecoreCache();
        iHecoreCache.put(new CacheObj("hecore","234"));
        iHecoreCache.put(new CacheObj("hecore","123"));
        System.out.println(iHecoreCache.get("hecore"));

当 

HecoreCacheManager hcm=new HecoreCacheManager(new CacheConf(180,true));

测试结果为 hecore:123

 

你可能感兴趣的:(中间件)