当前位置:文档之家› 分布式缓存技术方案

分布式缓存技术方案

一、背景在高并发的分布式的系统中,缓存是必不可少的一部分。

没有缓存对系统的加速和阻挡大量的请求直接落到系统的底层,系统是很难撑住高并发的冲击,分布式系统中缓存的设计是很重要的一环使用缓存的收益:●加速读写,缓存一般是内存操作,要比传统数据库操作要快的多●降低后端的负载。

缓存一些复杂计算或者耗时得出的结果可以降低后端系统对CPU、IO、线程这些资源的需求●本地缓存远端调用结果,减少服务间的调用,提升服务并发能力目前问题:●目前业务中对缓存的使用并不多,在这次直播活动中,组件性能瓶劲很多,有很大一部分是可以通过缓存加速的●疫情直播活动期间,几个核心服务由于人手、改造难度问题等,最后由罗陈珑做一了个缓存代理服务,把UC、EOMS的部分接口做了缓存代理,这些缓存本来应該由服务提供者来实现的●数据一致性问题,加了缓存之后,随之而来的就是数据一致性的问题,发现有数据不能及时更新●目前大家对缓存使用方式不太统一,有的组件使用本地JVM缓存时封装太复杂,出现问题不好定位,清除缓存也不好做二、目标●降低分布式缓存技术使用门槛,将分布式缓存框架作为微服务开发必备的脚手架,让开发者更易使用,避免因技术门槛而放弃使用缓存梳理核心业务,使用分布式缓存加速服务响应速度,降低服务负载三、分布式缓存方案3.1 @WafCacheable 缓存3.1.1 分布式缓存和本地jvm缓存为了提高接口能力,需要将一些频繁访问但数据更新频率比较低的放入缓存中,不要每次从数据库或其他耗时耗资源的数据源中取。

使用@WafCacheable 注解,缓存过期时间可以根据数据更新频率自由设定,不设置默认为2小时。

@WafCacheable 标记的方法被拦截后,数据获取的优先级:本地jvm缓存>redis缓存> 数据源(DB、RMI、其他耗时耗资源的操作)@WafCacheable使用场景:高频访问低频更新的数据注意:@WafCacheable 对同一个类里的内调方法(A调B, B上加注解不生效),如果直接用this.B(),加在B上的缓存不生效,需要使用${service}.B()调用(${service}指service实例)。

3.1.2 RMI缓存RMI(Remote Method Invocation)是指微服务提供的SDK中FeignClient方式申请的接口,具体参考/noteshare?id=dda3f2af3976689339c9ffa5ba0b0675 RMI上也可以加@WafCacheable注解。

使用场景:对于数据变化频率在2小时以上的,为了减少服务端的请求,提高接口性能缓存方式:本地jvm缓存作用域:客户端/消费者,拦截的包要注意,只拦截以下规则的包命名建议RMI上缓存时间不要超过同一接口service上的过期时间的1/2, 最好设置为1/4.3.1.3 关闭缓存方法1)配置文件中修改●在application.yml中加入waf:cache:enabled: false注意:生产环境禁止关闭缓存●调用rest接口关闭当前结点缓存2)管理接口动态设置3.1.4 缓存配置默认情况下缓存是开启状态,缓存队列大小3W,单个对象20K以下会放入jvm,否则只放入redis,当然也可以通过缓存模式可以强制指定放入jvm.application.yml 配置:waf:cache:enabled: true #控制缓存开关max-object-siz e: 30 #缓存单条对象最大上限大小,单位k b max-queue-si ze: 20000 #缓存队列上限3.1.5 缓存注解的使用3.1.5 手动使用缓存CacheService是封装了jvm缓存和redis缓存,在使用sdk操作缓存时,会同时往jvm和redis都放一份,部分api支持对象大于设置的单个对象大小上限可不放jvm. RedisService封装redis的基本操作,包含分布式锁的操作。

3.2 缓存清除/数据一致性问题缓存能极大的提高系统负载能力,但同时也引入新的问题,数据一致的问题,当一个业务规则或缓存的源数据发生变化时,一般要求被缓存的数据也需要立即更新为最新的数据(少数场景是对数据实时性不敏感或有一定的容忍度)3.2.1 缓存清理的难点●缓存key不确定。

通过@Wafcacheable注解设置的缓存,缓存key的生成策略为如果设置了自定义key 的前缀,则使用自定义key前缀+各个参数拼接而成,如果没有设置自定义key则使用包名+类名+方法名+各个参数拼接而成。

●缓存存在多个节点中。

缓存不仅有微服务提供者内部缓存,还有客户端(消费端)的缓存,一般是在rmi通过@Wafcacheable设置的。

缓存既有jvm缓存也有redis缓存。

jvm缓存存在于每个application中。

这样意味着需要清除所有微服务节点内部的jvm缓存及redis缓存,包括客户端(消费者端)缓存。

3.2.2 LRU+TTL算法缓存更新在被动更新缓存上是采用LRU+TTL组合算法实现缓存更新,也就是说只要缓存达到任一清除条件(缓存过期或LRU)都会被清除LRU算法,当缓存容量不足时(控制了缓存上限,避免OOM),淘汰最久没有被访问过的,始终保持热数据的缓存命中率TTL算法,也就是超时剔除,给缓存数据手动设置一个过期时间,当超过时间后,再次访问时从数据源重新加载并设回缓存期,3.2.3 主动通知缓存更新(立即生效)由上层应用在配置更新后,通过MQ消息通知给微服务,微服务各应用节点以广播消息的方式消费,清除本节点内的缓存。

适用场景:适用数据一致性要求高,对数据不一致容忍度低的场景优点:数据一致性高,实现方式简单,上层应用改动比较小,只需在缓存的数据发生变化后发送一条mq消息缺点:上层应用需要按照清除缓存规则在相应的增、删、改的功能处增加mq消息。

缓存key规则说明:"," 英文逗号表示且的关系,检索同一个key中包含的逗号分隔的多个关键字";" 英文分号表示或的关系,检索封号分隔的多个key.例如:city,010;getAllCity解释:⏹检索缓存key中同时包含city和010字符串的key⏹检索缓存key中包含getAllCity的key.3.3 内置缓存管理3.3.1 开关缓存请求地址/cache/edit请求类型GET接口描述手动控制缓存开关注意:不要轻意关闭整个缓存,目前生产上流量比较大,关闭整个缓存会对数据库有较大压力如需紧级让某个开关生效,可以调用清除缓存的方法单个清缓请求示例http://xxxxx/app/cache/edit?enable=close 关闭缓存http://xxxxx/app/cache/edit?enable=t 开启缓存响应示例true 开启缓存false 关闭缓存3.3.2 打印jvm缓存请求地址app/cache/stack 请求类型GET接口描述以json 格式输出全部jvm缓存,包括key及value 此方法可用于调试缓存内变量请求示例http://xxxxx/app/cache/stack响应示例响应示例{"code": 200,"message": "OK","result": {}}3.3.3 打印jvm缓存队列大小请求地址/cache/stack/size请求类型GET接口描述仅输出jvm内缓存key的个数请求示例http://xxxxx/app/cache/stack/size响应示例{ "size": 3963 }3.3.4 打印所有jvm缓存key请求地址/cache/stack/keys请求类型GET接口描述输出当前进程中所有jvm缓存的key请求示例http://xxxxx/app/cache/stack/keys响应示例3.3.5 模糊搜索缓存中的key请求地址/cache/keys/_search请求类型GET接口描述模糊搜索某个key, 大小写无关请求示例http://xxxxx/app/cache/keys/_search?q=com 响应示例3.3.6 清除全部jvm缓存请求地址/cache/clearjvm请求类型GET接口描述清除全部jvm内存注意:尽量不要使用清除全部jvm内存,可以使用清除特定缓存操作请求示例http://xxxxx/app/cache/clearjvm响应示例响应示例{"code": 200,"message": "OK","result": {}}3.3.7 清除指定key缓存请求地址/cache/clear请求类型POST接口描述清除缓存,可以清除多个缓存请求示例http://xxxxx/app/cache/clear body:["key"]响应示例响应示例{"code": 200,"message": "OK","result": {}}3.3.8 缓存状态请求地址/cache/states请求类型GET接口描述输出当前进程的缓存状态请求示例http://xxxxx/app/cache/states响应参数说明enabled 是否开启缓存maxQueueSize 缓存对列大小maxObjectSize 单个key允许对象最大大小,单位kb响应示例{"enabled": true, "maxQueueSize": 30000, "maxObjectSize": 20}3.3.9 获取缓存中某个key的值/cache/get请求地址GET请求类型输出key对应的值接口描述请求示/cache/get?key={key}例响应示例。

相关主题