如何设计缓存中间层

2023-05-16

目录

  • 需求描述:
  • 工程结构:
    • 截图
    • 代码
  • 工程配置:
    • pom.xml
    • application.yml
  • 缓存配置:
    • CacheConfig
    • CacheBase
    • CachePolicy
  • 如何使用:
    • User
    • IUserService
    • UserServiceImpl
    • UserServiceCache
    • UserController
  • 如何测试:
  • 数据分布:

需求描述:

传入主键集合获取对象列表,要求高命中率,主键类型可以是Integer、Long、String等类型,并且与业务解耦,不要求实时性。

工程结构:

截图

代码

链接:https://pan.baidu.com/s/1nO7fzPhSoA5WqNSmJ1dkrA
提取码:jv1p

工程配置:

pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.83</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.alicp.jetcache/jetcache-starter-redis -->
    <dependency>
        <groupId>com.alicp.jetcache</groupId>
        <artifactId>jetcache-starter-redis</artifactId>
        <version>2.6.7</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>31.1-jre</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

application.yml

jetcache:
  statIntervalMinutes: 1
  areaInCacheName: false
  local:
    default:
      type: linkedhashmap
      keyConvertor: fastjson
      limit: 100
  remote:
    default:
      type: redis
      keyConvertor: fastjson
      valueEncoder: java
      valueDecoder: java
      poolConfig:
        minIdle: 100
        maxIdle: 100
        maxTotal: 100
      host: 127.0.0.1
      port: 6379

缓存配置:

CacheConfig

仓库地址:https://github.com/alibaba/jetcache

@Configuration
@EnableMethodCache(basePackages = "com.example.demo.cache")
@EnableCreateCacheAnnotation
public class CacheConfig {}

CacheBase

核心思想:对于批量请求主键集合数据,首先到缓存中查询,将查询到的数据保存下来,然后用批量请求主键集合和刚才查询出来的数据主键集合做一个差值,如果差值为空,说明所有数据都命中了,此时只需要直接返回即可。如果差值存在,那么需要将差值主键集合传入到Lambda中,调用业务进行查找然后返回,我们需要对差值集合主键查询出来的数据,保存到缓存中,同时也要对空值进行处理,防止缓存穿透,以及要设置随机过期时间,防止缓存雪崩,最终返回查询结果。

public abstract class CacheBase {
    /**
     * 集合加载因子
     */
    public static final float DEFAULT_LOAD_FACTOR = 0.75f;

    /**
     * 获取缓存集合
     *
     * @return
     */
    public abstract Cache<String, String> getCacheMap();

    /**
     * 获取缓存策略
     *
     * @return
     */
    public abstract CachePolicy getCachePolicy();

    /**
     * 获取缓存数据
     *
     * @param guidList       业务主键集合
     * @param clazz          返回集合类型
     * @param keyMapper      获取对象主键
     * @param businessLoader 未命中主键查询
     * @param args           缓存键的键前缀
     * @param <P>            主键类型,例如:Integer、Long、String、...
     * @param <T>            对象类型,例如:User、Student、...
     * @return
     */
    public <P, T> List<T> getAll(List<P> guidList,
                                 Class<T> clazz,
                                 Function<? super T, ? extends P> keyMapper,
                                 Function<List<P>, List<T>> businessLoader,
                                 Object... args) {
        // 获取业务缓存策略
        CachePolicy policy = getCachePolicy();
        policy.inspect();
        String emptyObject = policy.getEmptyObject();
        int emptyObjectMinExpiredTime = policy.getEmptyObjectMinExpiredTime();
        int emptyObjectMaxExpiredTime = policy.getEmptyObjectMaxExpiredTime();
        TimeUnit emptyObjectExpiredTimeUnit = policy.getEmptyObjectExpiredTimeUnit();
        int plainObjectMinExpiredTime = policy.getPlainObjectMinExpiredTime();
        int plainObjectMaxExpiredTime = policy.getPlainObjectMaxExpiredTime();
        TimeUnit plainObjectExpiredTimeUnit = policy.getPlainObjectExpiredTimeUnit();
        // 创建最终返回集合
        List<T> resultList = new ArrayList(guidList.size());
        // 业务主键集合检查
        if (CollectionUtils.isEmpty(guidList)) {
            return resultList;
        }
        // 批量从缓存中查询
        int initialCapacity = (int) (guidList.size() / DEFAULT_LOAD_FACTOR + 1);
        Map<String, String> queryCacheMap = new HashMap<>(initialCapacity);
        List<Set<String>> matchKeyList = buildGuidMatchKeyList(guidList, args);
        for (Set<String> keySet : matchKeyList) {
            Map<String, String> returnMap = getCacheMap().getAll(keySet);
            if (!CollectionUtils.isEmpty(returnMap)) {
                queryCacheMap.putAll(returnMap);
            }
        }
        // 未命中的主键集合
        List<P> missGuidList = new ArrayList<>(guidList.size());
        if (queryCacheMap.keySet().size() > 0) {
            for (P guid : guidList) {
                String matchKey = buildGuidMatchKey(guid, args);
                String cacheValue = queryCacheMap.get(matchKey);
                // 缓存未命中
                if (StringUtils.isEmpty(cacheValue)) {
                    missGuidList.add(guid);
                    continue;
                }
                // 缓存被穿透
                if (emptyObject.equals(cacheValue)) {
                    continue;
                }
                resultList.add(JSON.parseObject(cacheValue, clazz));
            }
        } else {
            missGuidList = guidList;
        }
        // 如果全部命中
        if (CollectionUtils.isEmpty(missGuidList)) {
            return resultList;
        }
        // 设置初始大小
        initialCapacity = (int) (missGuidList.size() / DEFAULT_LOAD_FACTOR + 1);
        // 查询业务逻辑
        List<T> applyResultList = businessLoader.apply(missGuidList);
        Map<P, T> applyResultMap = applyResultList.stream().collect(Collectors.toMap(keyMapper, Function.identity()));
        Map<String, T> missGuidMap = new HashMap<>(initialCapacity);
        for (Map.Entry<P, T> entry : applyResultMap.entrySet()) {
            P guid = entry.getKey();
            T value = entry.getValue();
            String matchKey = buildGuidMatchKey(guid, args);
            missGuidMap.put(matchKey, value);
        }
        // 防止缓存穿透
        Map<String, String> nullMap = new HashMap<>(initialCapacity);
        for (P guid : missGuidList) {
            String matchKey = buildGuidMatchKey(guid, args);
            if (!missGuidMap.containsKey(matchKey)) {
                nullMap.put(matchKey, emptyObject);
            }
        }
        if (!CollectionUtils.isEmpty(nullMap)) {
            long expiredTime = getRandomNumber(emptyObjectMinExpiredTime, emptyObjectMaxExpiredTime);
            getCacheMap().putAll(nullMap, expiredTime, emptyObjectExpiredTimeUnit);
        }
        // 保存未命中的
        Map<String, String> saveMap = new HashMap<>(initialCapacity);
        for (Map.Entry<String, T> entry : missGuidMap.entrySet()) {
            String matchKey = entry.getKey();
            T value = entry.getValue();
            String cacheValue = JSON.toJSON(value).toString();
            saveMap.put(matchKey, cacheValue);
        }
        if (!CollectionUtils.isEmpty(saveMap)) {
            long expiredTime = getRandomNumber(plainObjectMinExpiredTime, plainObjectMaxExpiredTime);
            getCacheMap().putAll(saveMap, expiredTime, plainObjectExpiredTimeUnit);
        }
        // 返回最终结果
        resultList.addAll(applyResultMap.values());
        return resultList;
    }

    /**
     * 构建MatchKey集合
     *
     * @param guidList
     * @param args
     * @return
     */
    private <P> List<Set<String>> buildGuidMatchKeyList(List<P> guidList, Object... args) {
        // 获取业务缓存策略
        CachePolicy policy = getCachePolicy();
        policy.inspect();
        int batchSize = policy.getBatchSize();
        // 批量切分主键集合
        List<Set<String>> matchKeyList = new ArrayList<>();
        List<List<P>> partitionList = Lists.partition(guidList, batchSize);
        for (List<P> guids : partitionList) {
            Set<String> guidSet = new HashSet<>();
            for (P guid : guids) {
                guidSet.add(buildGuidMatchKey(guid, args));
            }
            matchKeyList.add(guidSet);
        }
        return matchKeyList;
    }

    /**
     * 构建MatchKey对象
     *
     * @param guid
     * @param args
     * @return
     */
    private <P> String buildGuidMatchKey(P guid, Object... args) {
        // 获取业务缓存策略
        CachePolicy policy = getCachePolicy();
        policy.inspect();
        String matchKeySplicer = policy.getMatchKeySplicer();
        // 构建缓存主键对象
        StringBuilder matchKey = new StringBuilder();
        for (Object arg : args) {
            matchKey.append(arg).append(matchKeySplicer);
        }
        matchKey.append(guid);
        return matchKey.toString();
    }

    /**
     * 获取指定范围数字
     *
     * @param lowerValue
     * @param upperValue
     * @return
     */
    private long getRandomNumber(int lowerValue, int upperValue) {
        return (long) Math.floor(Math.random() * (upperValue - lowerValue + 1) + lowerValue);
    }

    /**
     * 删除缓存数据
     *
     * @param guidList
     * @param args
     * @return
     */
    public <P> boolean removeAll(List<P> guidList, Object... args) {
        if (CollectionUtils.isEmpty(guidList)) {
            return false;
        }
        List<Set<String>> matchKeyList = buildGuidMatchKeyList(guidList, args);
        for (Set<String> keySet : matchKeyList) {
            getCacheMap().removeAll(keySet);
        }
        return true;
    }
}

CachePolicy

@Getter
@Setter
@ToString
public class CachePolicy {
    /**
     * 批量处理大小
     */
    private int batchSize;

    /**
     * 匹配键拼接符
     */
    private String matchKeySplicer;

    /**
     * 空值对象数据
     */
    private String emptyObject;

    /**
     * 空值对象数据:最小过期时间
     */
    private int emptyObjectMinExpiredTime;

    /**
     * 空值对象数据:最大过期时间
     */
    private int emptyObjectMaxExpiredTime;

    /**
     * 空值对象数据:过期时间单位
     */
    private TimeUnit emptyObjectExpiredTimeUnit;

    /**
     * 普通对象数据:最小过期时间
     */
    private int plainObjectMinExpiredTime;

    /**
     * 普通对象数据:最大过期时间
     */
    private int plainObjectMaxExpiredTime;

    /**
     * 普通对象数据:过期时间单位
     */
    private TimeUnit plainObjectExpiredTimeUnit;

    /**
     * 默认缓存策略
     */
    public CachePolicy() {
        this.batchSize = 100;
        this.matchKeySplicer = "_";
        this.emptyObject = "empty_object";
        this.emptyObjectMinExpiredTime = 5;
        this.emptyObjectMaxExpiredTime = 10;
        this.emptyObjectExpiredTimeUnit = TimeUnit.MINUTES;
        this.plainObjectMinExpiredTime = 5;
        this.plainObjectMaxExpiredTime = 10;
        this.plainObjectExpiredTimeUnit = TimeUnit.MINUTES;
    }

    /**
     * 检验缓存策略
     */
    public void inspect() {
        if (this.batchSize <= 0) {
            throw new IllegalArgumentException("batchSize Must be greater than 0");
        }
        if (this.matchKeySplicer == null) {
            throw new IllegalArgumentException("matchKeySplicer Must be not null");
        }
        if (this.emptyObject == null) {
            throw new IllegalArgumentException("emptyObject Must be not null");
        }
        if (this.emptyObjectMinExpiredTime <= 0) {
            throw new IllegalArgumentException("emptyObjectMinExpiredTime Must be greater than 0");
        }
        if (this.emptyObjectMaxExpiredTime <= 0) {
            throw new IllegalArgumentException("emptyObjectMaxExpiredTime Must be greater than 0");
        }
        if (this.plainObjectMinExpiredTime <= 0) {
            throw new IllegalArgumentException("plainObjectMinExpiredTime Must be greater than 0");
        }
        if (this.plainObjectMaxExpiredTime <= 0) {
            throw new IllegalArgumentException("plainObjectMaxExpiredTime Must be greater than 0");
        }
        if (this.plainObjectExpiredTimeUnit == null) {
            throw new IllegalArgumentException("plainObjectExpiredTimeUnit Must be not null");
        }
    }
}

如何使用:

User

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
    private Long id;
    private String name;
}

IUserService

public interface IUserService {
    /**
     * 根据主键集合获取用户列表
     *
     * @param ids
     * @return
     */
    List<User> selectList(List<Long> ids);
}

UserServiceImpl

@Slf4j
@Service
public class UserServiceImpl implements IUserService {
    /**
     * 模拟数据库中数据
     */
    static Map<Long, User> db = new HashMap<>();
    static {
        db.put(1L, User.builder().id(1L).name("张三").build());
        db.put(2L, User.builder().id(2L).name("李四").build());
        db.put(3L, User.builder().id(3L).name("王五").build());
        db.put(4L, User.builder().id(4L).name("赵六").build());
    }

    @Override
    public List<User> selectList(List<Long> ids) {
        log.info("UserServiceImpl selectList ids = {}", ids);
        List<User> result = new ArrayList<>();
        for (Long id : ids) {
            User user = db.get(id);
            if (user != null) {
                result.add(user);
            }
        }
        return result;
    }
}

UserServiceCache

@Slf4j
@Service
public class UserServiceCache extends CacheBase {
    /**
     * ==================================================
     * 全局缓存配置
     * ==================================================
     */
    private static final String AREA = "default";
    private static final String NAME = "user:list:";

    @CachePenetrationProtect
    @CreateCache(area = AREA, name = NAME, cacheType = CacheType.REMOTE, expire = 60)
    private Cache<String, String> matchCache;

    @Override
    public Cache<String, String> getCacheMap() {
        return this.matchCache;
    }

    @Override
    public CachePolicy getCachePolicy() {
        CachePolicy policy = new CachePolicy();
        policy.setBatchSize(100);
        policy.setMatchKeySplicer("_");
        policy.setEmptyObject("empty_object");
        policy.setEmptyObjectMinExpiredTime(5);
        policy.setEmptyObjectMaxExpiredTime(10);
        policy.setEmptyObjectExpiredTimeUnit(TimeUnit.MINUTES);
        policy.setPlainObjectMinExpiredTime(5);
        policy.setPlainObjectMaxExpiredTime(10);
        policy.setPlainObjectExpiredTimeUnit(TimeUnit.MINUTES);
        return policy;
    }

    /**
     * ==================================================
     * 业务缓存处理
     * ==================================================
     */
    @Autowired
    private IUserService userService;

    public List<User> selectList(List<Long> ids) {
        log.info("UserServiceCache selectList ids = {}", ids);
        return super.getAll(ids, User.class, User::getId, userService::selectList, "user", "id");
    }
}

UserController

@RestController
@RequestMapping("user")
public class UserController {
    @Autowired
    private UserServiceCache userServiceCache;

    @PostMapping("selectList")
    public List<User> selectList(@RequestBody List<Long> ids) {
        return userServiceCache.selectList(ids);
    }

    @PostMapping("removeList")
    public boolean removeList(@RequestBody List<Long> ids) {
        return userServiceCache.removeAll(ids, "user", "id");
    }
}

如何测试:

数据分布:

第一次访问,缓存中不存在,因此没有命中,hit=0,数据每隔1分钟统计一次,统计间隔可修改

第二次访问,缓存中已存在,因此肯定命中,hit=5,数据每隔1分钟统计一次,统计间隔可修改

以下内容则是数据在Redis中的分布情况,每个Key的前缀参数列表是user、id,_ 是参数连接符

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何设计缓存中间层 的相关文章

  • 【C++】weak_ptr弱引用智能指针详解

    weak ptr这个指针天生一副小弟的模样 xff0c 也是在C 43 43 11的时候引入的标准库 xff0c 它的出现完全是为了弥补它老大shared ptr天生有缺陷的问题 相比于上一代的智能指针auto ptr来说 xff0c 新进
  • 【C++】模板声明与定义不分离

    一般在写C 43 43 相关代码的时候 xff0c 我们总习惯于将类声明和类实现进行分离 也就是说 xff0c 类的声明一般写在 h文件中 xff0c 而它的实现一般写在 cpp文件中 但是 xff0c 在模板类中 xff0c 这个习惯却要
  • 【C++】空指针调用成员函数及访问成员变量

    最近在review代码的时候发现 xff0c 使用了空指针调用成员函数 xff0c 并且成员函数内部有使用到成员变量 xff0c 居然没有出错 很是奇怪 xff0c 就用一篇博客把关于空指针调用成员函数相关的内容总结起来 空指针调用成员函数
  • 【C++】两个类的相互引用

    有时候在设计数据结构的时候 xff0c 可能会遇到两个类需要相互引用的情形 比如类A有类型为B的成员 xff0c 而类B又有类型为A的成员 那么这种情形下 xff0c 两个类的设计上需要注意什么呢 xff1f 同一文件 尝试方案 将A和B的
  • 【滤波】离散贝叶斯滤波器

    本文参考自rlabbe Kalman and Bayesian Filters in Python的第2章节02 Discrete Bayes xff08 离散贝叶斯 xff09 span class token operator span
  • 用iPad编写C/C++代码(计算机考研党也能用iPad写算法题)

    下载iSH软件 1 在AppStore商店中下载名叫iSH Shell的软件 PS xff1a iSH是一个使用用户模式x86模拟器在iOS设备上获得本地运行的Linux Shell环境的项目 2 安装后点开iSH xff0c 初步了解iS
  • FreeRTOS数据结构——列表与列表项

    在FreeRTOS中存在着大量的基础数据结构列表和列表项的操作 xff0c 要想读懂FreeRTOS的源代码 xff0c 就必须弄懂列表和列表项的操作 一 C语言链表简介 链表就好比一个圆形的晾衣架 xff0c 晾衣架上有很多钩子 xff0
  • ROS1常见问题及解决办法——ROS依赖包安装问题

    ROS依赖包安装问题 问题描述解决方案 问题描述 在ROS编译过程中经常会遇到找不到ROS包的情况 xff0c 如下所示 CMake Error at span class token operator span opt span clas
  • Attention Model(mechanism) 的 套路

    最近刷了一些attention相关的paper 照着here的列表 43 自己搜的paper xff0c 网上相关的资料也有很多 xff0c 在此只讲一讲自己对于attention的理解 xff0c 力求做到简洁明了 一 attention
  • 程序=数据结构+算法

    数据 由 数据元素 组成 xff0c 数据元素 由 数据项 组成 数据元素 xff1a 组成数据的基本单位 xff0c 是集合的个体 数据对象 xff1a 性质相同的数据元素的集合 xff0c 是数据的一个子集 数据结构 xff1a 数据元
  • ROS为上位机与STM32为下位机串口通讯(一)

    STM32通过串口向ROS上位机发送信息 主要实现了STM32 通过串口向ROS上位机发送数据 xff0c 发布者将接收到的数据发布出去并打印出来 xff0c 订阅者订阅发布者发布的消息并打印出来 xff0c 最后通过roslaunch启动
  • 程序员面试真的全都答对就有offer?

    程序员面试 xff0c 技术水平重要吗 xff1f 只要面对面试官 xff0c 估计大家都认为技术水平最重要 xff0c 其他都是幌子 xff01 当然 xff0c 技术是基础 xff0c 但技术并不是全部 xff0c 而且一个面试者的技术
  • 解决vncserver打开远程桌面后没有图标,只有一个鼠标问题

    前言 介绍一个VNC客户端 IIS7服务器管理工具 作为VNC客户端 xff0c 它最优秀的功能就是支持一键导出或导入 xff0c 一键批量打开VNC xff0c 一键批量关闭VNC xff0c 多台VNC 自定义备注 xff0c 自定义分
  • openmv的串口传输

    import sensor image time from pid import PID from pyb import Servo from pyb import UART uartCP 61 UART 1 115200 用于电脑与OPe
  • 三层交换机的“三层”是什么意思?

    关注我们的朋友应该知道 xff0c 之前我们简单分析过二层工业交换机和三层工业交换机的区别 但是近期有客户向我们咨询 xff0c 工业交换机中的三层是指什么意思 xff1f 是指工业交换机中有三层 东西 吗 xff1f 就这个问题我们一起来
  • 外贸人SOHO怎么收汇?2020最新外贸B2B收款结汇方法详解!

    很多做外贸朋友都知道 xff0c 外贸收款 结汇是外贸交易中非常重要的一个环节 一个好的外贸收款渠道 xff0c 可以快速地帮助企业资金回笼 xff0c 支付货款 退税等等 xff0c 能省去很多不必要的麻烦 所以 xff0c 对于外贸从业
  • 在回收站删除的文件怎么恢复

    问题描述 xff1a 回收站清空是很常见的数据恢复故障 在回收站删除的文件怎么恢复接下来我们还需要了解下具体如何恢复回收站清空的资料 xff0c 具体请看正文了解 工具 软件 xff1a 极限数据恢复软件 步骤1 xff1a 先百度搜索并下
  • Jenkins修改显示语言为中文

    最近在Centos7 4里安装了jenkins 2 235 1版本 xff0c 使用的推荐安装插件 xff0c 安装后发现部分中文简体不翻译的情况 一 解决方法 xff1a 默认初始安装的时候已经安排了插件Localization Chin
  • 第三章 九析带你轻松完爆 MinIO - MinIO 客户端使用(mc)

    目录 1 前言 2 邀约 3 介绍 4 mc 安装 5 操作 5 1 查看 minio server 5 2 添加 minio server 5 3 删除 minio server 5 4 查看 minio server 中 bucket
  • 分享一款巨微MS1586低功耗蓝牙芯片

    MS1586包含8位单片机和低功耗 低成本的BLE收发器 xff0c 内部集成了发射机 接收机 GFSK调制解调器和BLE基带处理 遵循BLE广播通道通信 xff0c 具有成本低 体积小 控制方便等优点 特点 4KWOTPROM 256by

随机推荐

  • 推荐系统经典论文文献及业界应用

    Survey方面的文章及资料 Adomavicius G Tuzhilin A Toward the next generation of recommender systems A survey of the state of the a
  • 群辉web station开启http访问文件功能

  • centos7.6 快速架设kiftd私有网盘 文件管理系统

    一 基础环境 安装源ISO CentOS 7 x86 64 DVD 1810 最小化安装系统后先更新 root 64 Server yum update y root 64 Server cat etc redhat release Cen
  • 微信公众号开发

    文章目录 第一章 环境准备1 1 开发工具1 2 创建工程1 3 添加依赖1 4 添加模板1 5 测试接口1 6 内网穿透1 7 接入指南 第二章 基础支持2 1 获取 AccessToken 令牌2 2 获取微信API接口IP地址2 3
  • 果然新鲜电商系统

    项目简介 果然新鲜电商系统是一个类似小米商城的B2C电商平台 xff0c 可做毕业设计参考 访问地址 xff1a https gitee com caochenlei fresh parent 项目截图 网站首页 本地访问 xff1a ht
  • 通用代码生成工具

    项目简介 CodeBuilder可以帮助你快速生成模板文件 xff0c 目前支持mysql oracle sql server数据库 您可以自己制作代码模板并添加到模板目录 xff0c 帮助您可以应付各种开发场景 访问地址 xff1a ht
  • 后台权限管理系统

    项目简介 CommonAdmin是一个按钮级权限管理系统 xff0c 包含企业后台最常用的系统模块 xff0c 代码简洁 xff0c 开箱即用 访问地址 xff1a https gitee com caochenlei common adm
  • 个人博客管理系统

    项目简介 Blog是一款个人博客管理系统 xff0c 是我和同学上学期的期末大作业 xff0c 完成的比较仓促 xff0c 大部分功能已经完成 访问地址 xff1a https gitee com caochenlei blog 主要页面
  • Docker的学习与使用

    目录 第一章 Docker介绍第二章 Docker架构第三章 Docker安装第四章 Docker进程相关命令第五章 Docker镜像相关命令第六章 Docker容器相关命令第七章 Docker容器的数据卷第八章 Docker常见应用部署8
  • Java工程师的进阶之路

    目录 知识点01 xff1a 九大排序算法知识点02 xff1a 二分查找算法知识点03 xff1a 二叉树的遍历知识点04 xff1a Spring IOC知识点05 xff1a Spring AOP知识点06 xff1a Spring
  • 在线代码执行系统

    目录 第一章 安装ubuntu第二章 安装SSH第三章 安装docker第四章 安装docker compose第五章 安装judge0 第一章 安装ubuntu 虚拟机 xff1a VirtualBox 6 1 30 148432 Win
  • TF-IDF

    1 TF IDF是什么 xff1f TF IDF xff1a term frequency inverse document frequency 1 tf idf 作为一种权重经常被用作信息检索和文本挖掘领域 2 这样一种权重时通过统计计算
  • 时间区间拆分算法

    目录 需求描述 xff1a 项目依赖 xff1a 代码实现 xff1a 运行效果 xff1a 需求描述 xff1a 时间范围 xff1a 2022 04 10 09 00 00 2022 04 12 18 00 00 具体描述 xff1a
  • 时间区间合并算法

    目录 需求描述 xff1a 项目依赖 xff1a 代码实现 xff1a 运行效果 xff1a 需求描述 xff1a 时间范围 xff1a 2022 04 10 09 00 00 2022 04 10 10 00 00 2022 04 10
  • 如何排查线上OOM

    目录 操作步骤 xff1a 其他知识 xff1a 操作步骤 xff1a 换目录进行以下操作 xff08 不要在 操作 xff09 span class token builtin class name cd span 安装WGET下载工具
  • 计算饼状图百分比

    目录 需求描述 xff1a 项目依赖 xff1a 代码实现 xff1a 运行效果 xff1a 需求描述 xff1a 给定一个整数数组 xff0c 例如 xff1a 2 3 4 xff0c 计算各个元素的百分比 xff0c 要求百分比累加为1
  • 我是如何解决码云图床失效问题?

    目录 第一章 购买资源包第二章 配置存储桶第三章 上传图片集第四章 替访问域名第五章 配置Typora 第一章 购买资源包 第一步 xff1a 登录阿里云账号第二步 xff1a 访问资源包管理 第三步 xff1a 购买资源包套餐 第四步 x
  • Discord机器人开发

    目录 第一章 Discord账号注册第二章 Discord创建服务器第三章 Discord创建机器人3 1 创建应用3 2 创建机器人3 3 配置机器人3 4 添加机器人 第四章 Discord机器人开发准备4 1 推荐资料4 2 创建工程
  • 如何设计事件管理器

    目录 需求描述 xff1a 项目依赖 xff1a 代码实现 xff1a 定义通用的事件对象定义事件监听器接口定义事件监听器适配器对象定义事件管理器接口定义默认的事件管理器对象创建三个不同的监听器对象 运行效果 xff1a 需求描述 xff1
  • 如何设计缓存中间层

    目录 需求描述 xff1a 工程结构 xff1a 截图代码 工程配置 xff1a pom xmlapplication yml 缓存配置 xff1a CacheConfigCacheBaseCachePolicy 如何使用 xff1a Us