08|缓存操作:如何为接口优雅地提供缓存功能?
缓存疑惑
- 深入了解
- 翻译
- 解释
- 总结
本文深入探讨了在Dubbo框架中如何优雅地为接口提供缓存功能。首先描述了移动端App在首页加载时频繁向网关发起请求,导致页面卡顿和渲染缓慢的问题。经过排查,发现是权限系统响应时间增长导致的数据库查询压力过大。为了改善用户体验,架构团队决定在网关增加缓存功能。文章解释了增加缓存能提升接口响应时效的原因,通过比较不同存储媒介的延时关系,阐述了缓存对接口响应时间的减少具有质的飞越的作用。接着,文章介绍了在网关增加缓存功能的处理思路,包括简单处理和统一处理两种方式。在简单处理中,通过在网关增加一层缓存模块,减少远程调用次数,从而降低权限系统查询数据库的频次,提升性能。最后,文章提出了对简单处理方式的改进,通过统一处理方式提炼出通用的缓存调用结果的方法,避免重复代码和坏代码味道的产生。文章还通过源码解析的方式展示了Dubbo框架中如何实现缓存功能,为读者提供了清晰的实践指导。文章还讨论了改造方案可能带来的影响,包括内存容量、缓存数据刷新和过期剔除、流量峰值可能引发的缓存雪崩、穿透、击穿等问题。通过实际案例和技术原理,深入浅出地介绍了如何为接口提供缓存功能,对于需要优化接口性能的开发人员具有一定的参考价值。文章内容丰富,涵盖了缓存操作的应用、总结和思考题,为读者提供了全面的技术知识和实践经验。
《Dubbo 源码剖析与实战》,新⼈⾸单¥59
全部留言(7)
- 最新
- 精选
- RocketMQ为什么lru的方式下第二次和第三次调用结果相同,也就是第二次相同的调用才会缓存下来?但jcache方式下三次都是一样的
作者回复: 你好,RocketMQ:关于 lru 的这个细节现象,观察的非常仔细,问的非常好。 关于 lru 在存储层面,具体的实现类是 LRU2Cache 这个类,同时这个 LRU2Cache 也是继承了 LinkedHashMap,说明 LRU2Cache 自己本身就是一个 Map 类型的派生类,但是呢 LRU2Cache 自己也持有了一个【private PreCache<K, Boolean> preCache】变量,该变量的容量默认是 1000。 可以看看这段 get/put 的代码: ======================== @Override public V get(Object key) { lock.lock(); try { return super.get(key); } finally { lock.unlock(); } } @Override public V put(K key, V value) { lock.lock(); try { if (preCache.containsKey(key)) { // add it to cache preCache.remove(key); return super.put(key, value); } else { // add it to history list preCache.put(key, true); return value; } } finally { lock.unlock(); } } ======================== LRU2Cache 在 get 的时候,总是从 LRU2Cache 自己的父类中去 get 数据,但是在 put 的时候,针对同一个 key 进行第二次 put 的时候才会放到 LRU2Cache 自己的父类中去。 这其实是 LRU2Cache 自身针对不常用的缓存数据一种考量,若请求的结果 R1 调用的频率极低的时候,那么 LRU2Cache 就会考虑优先将 R1 放到 preCache 中,在以后的 999 次请求中,R1 若还会被用到的话,那就会将 R1 转移到 LRU2Cache 的父类缓存中去;若在以后的 999 次请求中还是不会用到 R1 的话,那么就只好依赖 PreCache#removeEldestEntry 方法移除这种百八十年不动的“僵尸冷数据”。
2023-03-17归属地:广东1 - 飞飞实际开发真的会真么用吗?一般不都是服务提供者直接在自己的业务逻辑里面使用redis等直接缓存结果吗?不需要配置这么多东西呀?
作者回复: 你好,飞飞:实际开发这个问的好,我只是众多实际开发中的一员,不能以点代面。看不同开发人员的诉求,如果不想引入其他框架或不想写,也可以直接使用 dubbo 的,又或者有些公司主推使用 dubbo 特性啥的,也可能有其他特别喜好使用 dubbo 人员啥的,这个都不不好说,具体场景具体问题,因地制宜看待罢了,只要适合自己的就好。
2023-05-30归属地:北京2 - Lum这个dubbo中的cache支持过期时间等定制化功能吗 看cache的接口只有两个方法
作者回复: 你好,Lum:这个我教你一个墨守成规的快速找到的方式,在源码的【org.apache.dubbo.cache】包下,进行【Ctrl + Shift + F】快捷键搜索,然后输入【"cache.】这样的内容,然后就可以快速看到哪些是与时间相关的配置参数键了。
2023-03-05归属地:北京 - 杨老师在平时工作中,可能不会通过@DubboReference(cache = "jcache")来引入redis。 而是就直接使用redis了。 这俩方案没啥区别吧?而且第一种好像更麻烦些了
作者回复: 你好,杨老师:这个看不同人的喜好吧~如果是少部分接口,或者是少部分系统内部的功能,需要使用缓存的话,怎么书写都无大碍。 但是如果一旦这种少部分功能的慢慢累积增多时,就得慢慢考虑雷同重复代码的抽象封装了,至于是考虑在Dubbo层面扩展,还是自行横切拦截扩展,那只是不同的方式而已。 而直接在 Dubbo 层面扩展,意思就很明确,缓存的粒度,就是调用下游的接口,或者系统暴露的接口,比较精准聚焦。 而除了需要对Dubbo的接口进行缓存,还需要对一些数据库的查询操作,系统内的业务聚合操作做缓存的话,那么Dubbo框架就无法满足了,这个时候就得考虑自定义横切拦截了。
2023-02-22归属地:北京2 - 星期八服务端提供缓存功能是为了缓存方法调用后的结果的,在下一次客户端调用过来,服务端只需要取缓存的吗?
作者回复: 你好,星期八:是的,我们可以试着反推一下,如果缓存调用前的数据,可是调用之前还不知道调用结果,那缓存什么呢?缓存空对象么?如果缓存空对象的话,那么当接收消费方请求时,提供方发现有一个空对象缓存,如果把空对象返回给消费方,从功能的角度来说,功能是有缺失的,不加缓存还能拿到数据,加了缓存反而拿到一个空对象,明显不是消费方想要的结果。
2023-01-15归属地:浙江 - 孙升if (iterator.hasNext()) { CachingProvider provider = iterator.next(); if (iterator.hasNext()) { throw new CacheException("Multiple CachingProviders have been configured when only a single CachingProvider is expected"); } else { return provider; } } else { throw new CacheException("No CachingProviders have been configured"); } 自问自答了,这里会校验是否有多个实现类
作者回复: 你好,Mandone:哈哈,好像是哦,自问自答啦,没关系啦,发现问题,找到问题,解答疑惑,这挺好的,非常棒~
2023-01-06归属地:广东 - 孙升如果同时引入除redisson支持Jcache规范的其他maven包会怎么样?如何判断要使用哪个包的Cache呢?是通过spi配置吗
作者回复: 你好,Mandone:在这个 javax.cache.Caching.CachingProviderRegistry#getCachingProvider(java.lang.ClassLoader) 方法中,会进行判断,如果利用 SPI 机制加载 CachingProvider 的所有实现类后,发现有多个实现类则会抛出异常的,抛出的异常信息代码为:throw new CacheException("Multiple CachingProviders have been configured when only a single CachingProvider is expected")。
2023-01-06归属地:广东