业务自定义缓存框架实现

概述

为什么要多级缓存呢?无外乎以下几点:

  • Redis挂了是不可用了, 所有请求全部打到DB ,容易导致缓存雪崩
  • 访问Redis会有序列化和分序列化开销、网络IO成本,虽然性能很高但是终究没有本地缓存快,参考计算机系统中使用L1,L2,L3多级缓存,用来减少对内存的直接访问,从而加快访问速度。
    常见缓存使用姿势大体如下:

    以上若每个方法都硬编码实现,则代码重复度太高,一般都需要使用 AOP切面手段减少代码冗余。接下来具体介绍下我司的解决方案。我司目前对缓存的定制化需求比较高,因此spring原生态的缓存注解不满足要求,因此我们都是自己研发的。大体流程如下:

具体实现

当初实现该框架要满足我们业务的快速迭代,并且是代码无侵入的,必须是一行注解就搞定所有事情,一切都是配置化。
先看我们的 appolo缓存元数据设计
核心配置详解:
type: 缓存类型,

  • LOCAL 只使用本地缓存
  • REDIS 只使用 Redis cluster缓存
  • NONE 不使用缓存 (该namespace对应的查询直接穿透缓存)
  • LOCAL_AND_REDIS 本地缓存+redis缓存

cacheNullValue : 是否开启缓存穿透保护,开启后对于攻击请求即使DB不存在也会把空值存储在缓存中。
localLimit : 本地缓存的个数 避免本地缓存产生OOM,本地缓存一般是LRU淘汰机制算法
localExpire: 本地缓存过期时间
redisExpire: Redis缓存过期时间
redisRandomOffset: Redis缓存过期随机振幅,避免缓存击穿
nullValueRedisExpire: NULL值存储过期时间,空值一般过期时间比较短。
以上配置全部在分布式配置中心中,支持配置热加载实时生效。
通过以上配置实现了本地缓存、分布式缓存开关的隔离,并且支持按照namespace开启或者关闭缓存,甚至支持全局开启或者关闭缓存。
每个namespace 不同的缓存配置策略,能够做到不同业务不同的缓存策略。

支持默认 namespace ,比如 defaultCacheConfig KEY 定义的就是默认namespace,当注解声明的方法没有强制指定使用那个namespace时,默认使用 defaultCacheConfig 对应的配置策略。
以上配置全部放到配置中心, 做到热加载能随时改变不同的缓存策略,即时生效。

缓存注解关键字段:
name : 缓存名称
keys : 缓存参数KEY , 这些值会拼凑成唯一缓存KEY
secondKey : KEY 增强,当上述自定义KEY有可能重复时

整体操作流程

支持自定义强制缓存穿透和refresh缓存

支持按请求/调用链穿透缓存
http请求可以设置Cache-Penetration来强制设置本次请求不使用缓存。 穿透的含义是强制不走缓存,强制不走缓存查询;查询的时候不走缓存,也不把最终结果保存到缓存去
设置方式:

方式 伪代码 说明
添加header项 header["Cache-Penetration"] = {value} value值为0/1/2/3,详见下文
添加请求参数 ?k=v&Cache-Penetration={value}

value取值参考:

含义 备注
0 不穿透缓存(默认)
1 穿透本地缓存
2 穿透redis缓存
3 穿透本地缓存和redis缓存

简洁使用如下,请求header中传递,统一拦截把值存入 threadLocal , 在缓存处理切面当发现对应的缓存穿透时则不再从对应的缓存中获取,直接调用真实业务逻辑。

支持按请求/调用链重置缓存
http请求可以设置Cache-ForceCleanByRoute来强制清除缓存,读取最新业务结果刷新缓存,强调fresh
主要使用场景:遇到问题需要快速清缓存。
设置方式:

方式 伪代码 说明
添加header项 header["Cache-ForceCleanByRoute"] = {value} value值为true/false,详见下文
添加请求参数 ?k=v&Cache-ForceCleanByRoute={value}
value取值参考:
含义 备注
true 强制刷新本次请求所有涉及到的缓存(包括localCache和redis)
false 不会造成任何影响

简洁使用如下,请求header中传递,统一拦截把值存入 threadLocal , 在缓存处理切面当发现取消强制清理缓存时,就把对应的缓存删除

缓存使用情况统计

caffeine有专门的统计类, Redis cluster的统计我们是重写 StatsCounter 使用 ConcurrentStatsCounter 自定义实现。

缓存一致性考虑

  1. 弱一致性: 通过过期时间控制
  2. 中一致性:配置缓存刷新周期
  3. 强一致性: 接入 binlog回放平台触发

本地缓存使用时, 根据 CAP 理论到 P 很多时, C 肯定会降低,会出现各种不一致的问题, 故我们这边对一些强一致场景在NGINX层进行一致性HASH 路由,保证相同的KEY打到同一台机器, 减少了P 提高了 C。

其他特性

  1. 我们做到了框架中对于集合参数的结果单独缓存,避免整个缓存提高缓存利用率
  2. 支持更新失败数据延期以兜底,框架配置化支持当服务不可用时,缓存自动续期,相当于降级兜底
  3. 支持缓存主动更新(预取), 框架配置化支持,配置刷新周期
  4. 框架配置化支持更新时本地锁+ 分布式锁 主要一些重操作高并发的时候减少消耗
原文地址:https://www.cnblogs.com/yyystar/p/15475043.html