兜底是服务高可用的有效手段之一,一般用于对业务要求较高,服务可用性较强的场景
1 背景
当前业务遇到白屏等问题,使用兜底缓存能解决该问题,提升用户体验。
2 功能
- 程序抛出异常或返回结果为null时,返回历史缓存的兜底数据(通过@SpareCached控制缓存条件)
- 兜底数据采样规则、存储方式可扩展;兜底数据采样规则: 默认提供按比例采样,存储方式:默认提供hashmap、caffeine两种。
- 用户可自定义静态方法或非静态方法来处理兜底逻辑。
3 组件使用方式
3.1 配置文件增加如下配置
xx-boot: spare-cache: caches: - name: cache1 #兜底缓存名称 sample-matcher: #数据采样匹配器 type: default #默认匹配器 (可扩展) props: intervalBetweenSamples: 100 store-way: #兜底数据存储方式(可扩展) type: caffeine #caffeine props: spec: maximumSize=1000,expireAfterWrite=100s (具体查看CaffeineSpec#configure方法) - name: cache2 sample-matcher: type: default props: intervalBetweenSamples: 100 store-way: type: map props: referenceType: strong/soft/weak (默认strong,不建议使用weak) |
3.2 代码方法中增加注解
@SpareCached(name="spareCacheName", key = "#root.methodName+'_'+#name") public String test(String name){ return name; } |
备注: 可以与@SentinelResource、@Cacheable整合使用
3.3 spare
Cached注解属性
注解属性 | 含义 |
String name() default "default"; | 对应配置文件中的hexin-boot |
String key(); | 存入兜底缓存的key, 支持SpEL |
boolean handleWithNullResult() default false; | 返回结果null是否需要处理 |
boolean handleWithException() default true; | 是否需要处理异常,true: 需处理,返回兜底缓存值,如果无兜底缓存抛出异常; false: 不需要处理,直接抛出异常 |
Class<? extends Throwable>[] exceptionsToTrace() default {Throwable.class}; | 需要处理的异常类, 当handleWithException=true时生效 |
Class<? extends Throwable>[] exceptionsToIgnore default {}; | 需要忽略处理的异常类, 当handleWithException=true时生效 |
String condition() default ""; | 符合条件的缓存, 默认都缓存, 支持SpEL |
String unless() default ""; | 符合条件的不缓存, 默认都缓存, 支持SpEL |
Class<?> customHandlerClass() default Void.class; | 自定义处理类,不指定指的是当前类,可以使用非static方法; 指明只能使用static方法 |
String customHandlerMethod() default ""; | 自定义处理方法,like: public <T> T handle(Object... args, Throwable e); |
4 兜底缓存逻辑图
兜底缓存不依赖sentinel、cache,能独立使用;但是可以与sentinel、cache整合使用
5 代码结构图
SpareCachedAspect 切面处理类
ISpareCacheManger 兜底缓存管理
SpareCache 兜底缓存对象
IKvStore 兜底缓存数据存储器
ISampleMatcher 数据采样匹配器
6 监控指标项
监控指标项 | 说明 |
方法总调用次数 | 维度:名称、方法 |
兜底执行次数 | 维度:名称、方法 |
兜底成功次数 | 维度:名称、方法 |
兜底失败次数(抛出异常、返回null) | 维度:名称、方法 |
兜底逻辑执行时间(ns) | 维度:名称、方法 |
兜底逻辑执行最大时间(ns) | 维度:名称、方法 |
兜底缓存数量 | 维度:兜底缓存名 |