12306项目学习笔记(框架篇cache)

2024-09-03 08:04

本文主要是介绍12306项目学习笔记(框架篇cache),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

BloomFilterPenetrateProperties

@ConfigurationProperties(prefix = BloomFilterPenetrateProperties.PREFIX): 指定这个类的属性会从配置文件中读取,前缀为 framework.cache.redis.bloom-filter.default

    /*** 每个元素的预期插入量*/private Long expectedInsertions = 64L;/*** 预期错误概率*/private Double falseProbability = 0.03D;

这两个参数都会直接影响布隆过滤器创建的数组长度。错误概率还会影响使用的哈希算法的个数。

概率设置过大会显著增加误判几率,过小会显著增加判断时间,性能较差。需要合理的去设置。

RedisKeySerializer

@RequiredArgsConstructor接收所有final字段的参数并按照
    private final String keyPrefix;private final String charsetName;private Charset charset;

为顺序将参数值传入。

RedisKeySerializer 类是一个自定义的 Redis 键序列化器,实现了 RedisSerializer<String> 接口,用于处理 Redis 中键的序列化和反序列化。

  • InitializingBean: 这个接口要求实现 afterPropertiesSet() 方法,在 Spring 容器初始化 Bean 后被调用。
  • RedisSerializer<String>: 这个接口定义了序列化和反序列化的方法,用于处理 Redis 中的键。

private final String keyPrefix;

  • Redis 键的前缀,用于在序列化时为每个键添加特定的前缀,以避免键冲突。

private final String charsetName;

  • 字符集名称,用于在反序列化时将字节数组转换为字符串。

private Charset charset;

  • Charset 对象,用于在反序列化时指定字符集。

serialize(String key)

  • 功能: 将给定的字符串键序列化为字节数组。
  • 实现: 在序列化过程中,将 keyPrefix 和传入的 key 连接起来,然后调用 getBytes() 方法将其转换为字节数组。

deserialize(byte[] bytes)

  • 功能: 将字节数组反序列化为字符串。
  • 实现: 使用指定的字符集将字节数组转换为字符串。、

afterPropertiesSet()

  • 功能: 在 Bean 初始化后调用,用于设置字符集。
  • 实现: 根据 charsetName 初始化 charset 字段。

RedisDistributedProperties

RedisDistributedProperties 类是一个用于配置 Redis 相关属性的 Java 类,使用了 Spring Boot 的 @ConfigurationProperties 注解。这个类的主要目的是将配置文件中的 Redis 相关属性映射到 Java 对象中,以便在应用程序中使用。

  • public static final String PREFIX: 定义了配置属性的前缀,便于在其他地方引用。
  • private String prefix: Redis 键的前缀,默认为空字符串。这个前缀可以用于区分不同模块或应用的数据,避免键冲突。
  • private String prefixCharset: Key 前缀的字符集,默认为 "UTF-8"。这个属性指定了在序列化和反序列化过程中使用的字符集。
  • private Long valueTimeout: 默认的超时时间,默认为 30000L(30秒)。这个属性用于设置 Redis 中存储数据的过期时间。
  • private TimeUnit valueTimeUnit: 时间单位,默认为 TimeUnit.MILLISECONDS。这个属性指定了 valueTimeout 的单位,可以是秒、毫秒等。

在 application.yml 或 application.properties 文件中,可以通过以下方式配置 Redis 的属性:

framework:cache:redis:prefix: "myapp:"prefixCharset: "UTF-8"valueTimeout: 60000  # 60秒valueTimeUnit: SECONDS

CacheAutoConfiguration

@AllArgsConstructor

@AllArgsConstructor 注解时,Lombok 会自动生成一个构造函数,该构造函数包含类中所有的字段作为参数。

在 Spring 应用中,使用 @AllArgsConstructor 可以方便地创建带有依赖的 Bean。例如,在配置类中注入多个属性时,可以使用构造函数注入,确保所有依赖项在创建时都被提供。

@EnableConfigurationProperties

  • 启用对 RedisDistributedProperties 和 BloomFilterPenetrateProperties 类的配置属性支持。这意味着这两个类中定义的属性可以通过配置文件进行设置。

创建 Redis Key 序列化器,可自定义 Key Prefix

    @Beanpublic RedisKeySerializer redisKeySerializer() {String prefix = redisDistributedProperties.getPrefix();String prefixCharset = redisDistributedProperties.getPrefixCharset();return new RedisKeySerializer(prefix, prefixCharset);}

创建默认布隆过滤器的 Bean

  • @ConditionalOnProperty 注解: 这个注解用于条件性地创建 Bean。它检查配置文件中是否存在特定的属性,并根据该属性的值决定是否创建该 Bean。在这里,它检查 framework.cache.redis.bloom-filter.default.enabled 是否设置为 true。只有在该属性为 true 时,cachePenetrationBloomFilter 方法才会被调用。
  • redissonClient.getBloomFilter(bloomFilterPenetrateProperties.getName()): 使用 Redisson 客户端获取布隆过滤器的实例。
  • 布隆过滤器的名称、预期插入量和误判概率是通过 BloomFilterPenetrateProperties 获取的。

这个默认的布隆过滤器其实在项目中并没有实际的使用,只有用户管理模块使用了一个重新定义的

userRegisterCachePenetrationBloomFilter,专门用于用户注册相关的逻辑。

stringRedisTemplateProxy

@Bean
// 静态代理模式: Redis 客户端代理类增强
public StringRedisTemplateProxy stringRedisTemplateProxy(RedisKeySerializer redisKeySerializer,StringRedisTemplate stringRedisTemplate,RedissonClient redissonClient) {stringRedisTemplate.setKeySerializer(redisKeySerializer);return new StringRedisTemplateProxy(stringRedisTemplate, redisDistributedProperties, redissonClient);
}
stringRedisTemplate.setKeySerializer(redisKeySerializer);
  • 设置键序列化器: 这行代码将 redisKeySerializer 设置为 stringRedisTemplate 的键序列化器。这意味着在进行 Redis 操作时,所有的键都会使用这个序列化器进行序列化和反序列化。
return new StringRedisTemplateProxy(stringRedisTemplate, redisDistributedProperties, redissonClient);

返回对象交给spring容器管理

CacheGetFilter<T>接口

boolean filter(T param);

解决布隆过滤器的不能删除的问题,但是目前没有实现具体逻辑。这是我的实现方法。

  1. 当用户注销时,将用户名添加到第一层布隆过滤器。
  2. 当用户注册时,先检查第二层布隆过滤器,如果用户名不在第二层过滤器中,则允许注册。
  3. 即使一个已注销的用户名被重新使用,也允许注册。
  4. 每天从数据库中检查已注销用户,重构第一层布隆过滤器。

CacheGetIfAbsent<T>接口

同样没有实现类,用于缓存未命中时,允许用户根据具体需求实现自定义的逻辑。

CacheLoader<T>接口

主要用于定义加载缓存的逻辑。它的设计目的是提供一种机制来从某个数据源(如数据库、外部服务等)加载数据并将其放入缓存中。

public class UserCacheLoader implements CacheLoader<User> {private final UserService userService; // 假设这是一个服务类,用于查询用户数据public UserCacheLoader(UserService userService) {this.userService = userService;}@Overridepublic User load() {// 从数据库加载用户数据return userService.findUserByUsername("exampleUser"); // 示例用户名}
}

CacheUtil

buildKey 方法

public static String buildKey(String... keys) {Stream.of(keys).forEach(each -> Optional.ofNullable(Strings.emptyToNull(each)).orElseThrow(() -> new RuntimeException("构建缓存 key 不允许为空")));return Joiner.on(SPLICING_OPERATOR).join(keys);
}

1. Stream.of(keys)

  • 功能:将传入的可变参数 keys 转换为一个流(Stream)。这使得可以对这些键进行流式操作。
  • 用途:流式操作提供了一种更简洁和可读的方式来处理集合或数组中的元素。

2. forEach(each -> ...)

  • 功能:对流中的每个元素(每个键)执行指定的操作。
  • 参数each 是流中当前处理的元素(即当前的键)。

3. Optional.ofNullable(Strings.emptyToNull(each))

  • Strings.emptyToNull(each)
    • 这个方法来自 Guava 库,它的作用是将空字符串转换为 null。如果 each 是空字符串,则返回 null;如果不是空字符串,则返回原始字符串。
  • Optional.ofNullable(...)
    • 将可能为 null 的值包装在 Optional 对象中。如果 each 为 null,则创建一个空的 Optional;如果 each 不为 null,则创建一个包含该值的 Optional

4. orElseThrow(...)

  • 功能:如果 Optional 是空的(即 each 为 null 或空字符串),则抛出异常。
  • 逻辑:在这里,使用 orElseThrow 方法来指定一个异常的生成逻辑。如果 Optional 中没有值,则抛出 RuntimeException,并提供错误消息 "构建缓存 key 不允许为空"

5.Joiner.on(SPLICING_OPERATOR).join(keys)

使用 Joiner.on(SPLICING_OPERATOR).join(keys) 将所有键连接成一个字符串,使用 _ 作为分隔符。

public static boolean isNullOrBlank(Object cacheVal) {return cacheVal == null || (cacheVal instanceof String && Strings.isNullOrEmpty((String) cacheVal));
}
  • 功能:判断给定的值是否为 null 或空字符串。
  • 参数:接受一个 Object 类型的参数 cacheVal
  • 逻辑
    • 检查 cacheVal 是否为 null,或者如果是字符串,检查其是否为空。
  • 返回值:返回一个布尔值,true 表示 cacheVal 为 null 或空字符串,false 表示不是。

CacheUtil 类提供了两个实用的静态方法,buildKey 用于构建缓存标识,isNullOrBlank 用于判断缓存值的有效性。

FastJson2Util

FastJson2Util 是一个工具类,主要用于构建 Java 泛型类型(Type)的实例,以便在使用 FastJSON 进行序列化和反序列化时能够正确处理泛型。

Cache 接口

Object getInstance();

获取底层缓存组件的实例。

DistributedCache 接口

DistributedCache 接口扩展了 Cache 接口,提供了一组用于分布式缓存的操作方法。这个接口主要用于处理缓存的获取、存储和管理,特别是在高并发和分布式环境中。

StringRedisTemplateProxy

StringRedisTemplateProxy 类是一个实现了 DistributedCache 接口的具体类,主要用于封装 Redis 操作,提供了一系列用于缓存的功能。这个类结合了 Redisson 和 Spring Data Redis 的功能,允许开发者在高并发环境下安全地管理缓存。

private <T> T loadAndSet(String key, CacheLoader<T> cacheLoader, long timeout, TimeUnit timeUnit, boolean safeFlag, RBloomFilter<String> bloomFilter) {T result = cacheLoader.load();if (CacheUtil.isNullOrBlank(result)) {return result;}if (safeFlag) {safePut(key, result, timeout, timeUnit, bloomFilter);} else {put(key, result, timeout, timeUnit);}return result;
}

从数据源加载数据并将其存入布隆过滤器。

@Override
public Boolean putIfAllAbsent(@NotNull Collection<String> keys) {DefaultRedisScript<Boolean> actual = Singleton.get(LUA_PUT_IF_ALL_ABSENT_SCRIPT_PATH, () -> {DefaultRedisScript redisScript = new DefaultRedisScript();redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource(LUA_PUT_IF_ALL_ABSENT_SCRIPT_PATH)));redisScript.setResultType(Boolean.class);return redisScript;});Boolean result = stringRedisTemplate.execute(actual, Lists.newArrayList(keys), redisProperties.getValueTimeout().toString());return result != null && result;
}
  • Lua 脚本:使用 Lua 脚本来实现原子操作。Lua 脚本在 Redis 中执行,可以确保在执行过程中不被其他操作打断。
  • Singleton.get(...):通过单例模式获取 Lua 脚本,确保脚本只被加载一次,提高性能。
  • setScriptSource:设置 Lua 脚本的来源,这里通过 ClassPathResource 加载脚本文件。
  • setResultType:指定脚本执行后返回的结果类型,这里为 Boolean.class
  • 执行脚本:通过 stringRedisTemplate.execute(...) 方法执行 Lua 脚本。
    • 参数
      • actual:要执行的 Lua 脚本。
      • Lists.newArrayList(keys):将传入的键集合转换为列表,以便传递给脚本。
      • redisProperties.getValueTimeout().toString():将超时时间作为参数传递给脚本(具体使用取决于脚本的实现)。
  • 返回值result 是脚本执行的结果,表示所有键是否成功放入缓存。
for i, v in ipairs(KEYS) doif (redis.call('exists', v) == 1) thenreturn nil;end
end
  • 遍历 KEYS:使用 ipairs 遍历 KEYS 表中的每个键。
  • 检查键是否存在:对于每个键 v,使用 redis.call('exists', v) 检查它是否存在于 Redis 中。
    • 如果 exists 命令返回 1,表示键存在,则立即返回 nil,表示操作失败。
for i, v in ipairs(KEYS) doredis.call('set', v, 'default');redis.call('pexpire', v, ARGV[1]);
end
  • 遍历 KEYS:再次遍历 KEYS 表中的每个键。
  • 设置键值对:对于每个键 v,使用 redis.call('set', v, 'default') 将其设置为默认值(这里使用 'default')。
  • 设置过期时间:使用 redis.call('pexpire', v, ARGV) 为键设置毫秒级过期时间。这里的 ARGV 是一个参数,表示过期时间。

这篇关于12306项目学习笔记(框架篇cache)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1132469

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

如何用Docker运行Django项目

本章教程,介绍如何用Docker创建一个Django,并运行能够访问。 一、拉取镜像 这里我们使用python3.11版本的docker镜像 docker pull python:3.11 二、运行容器 这里我们将容器内部的8080端口,映射到宿主机的80端口上。 docker run -itd --name python311 -p

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学