Dubbo 的缓存机制了解吗?
一则或许对你有用的小广告
欢迎加入小哈的星球,你将获得:专属的实战项目(4个项目都能学) / 1v1 提问 / 简历修改 / Java 学习路线 / 社群讨论 / 学习打卡 / 每月赠书
《Spring AI 项目实战(问答机器人、RAG 智能客服、联网搜索)》已完结,基于
Spring AI + Spring Boot 3.x + JDK 21...,查看介绍《从零手撸:仿小红书(微服务架构)》 已完结,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...,查看介绍;演示链接:http://116.62.199.48:7070/《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接:http://116.62.199.48/
新开坑项目:《从零手撸:秒杀系统高并发优化实战》 正在更新中...,查看介绍
截止目前,星球内专栏累计输出 150w+ 字,讲解图 5110+ 张,还在持续爆肝中.. 后续还会上新更多项目,已有 4700+ 小伙伴加入学习,欢迎点击围观
面试考察点
-
功能认知:面试官不仅仅是想知道 "Dubbo 有缓存" 这个事实,更是想确认你了解 Dubbo 在消费端提供了哪些缓存策略,以及每种策略的特点和区别。
-
实践意识:Dubbo 的缓存是消费端缓存,面试官想知道你是否清楚它适用的场景——比如哪些数据适合缓存、哪些不适合,生产环境有没有踩过坑。
-
架构判断力:Dubbo 自带的缓存比较轻量,面试官其实更想看你能不能判断什么时候该用 Dubbo 缓存,什么时候该上 Redis 等外部缓存,这才是架构层面的思考。
核心答案
Dubbo 提供了消费端(Consumer 端)的请求结果缓存,支持 4 种缓存策略:
| 缓存策略 | 实现类 | 特点 | 适用场景 |
|---|---|---|---|
lru |
LRUCache |
基于 LRU 算法,默认缓存 1000 个结果 | 默认策略,通用推荐 |
threadlocal |
ThreadLocalCache |
线程级缓存,当前线程内有效 | 同一次请求内多次调用同一服务 |
jcache |
JCache |
实现 JSR-107 规范,可对接 Ehcache 等 | 需要专业缓存框架的场景 |
expiring |
ExpiringCache |
基于过期时间的缓存 | 需要定时失效的场景 |
一句话结论:Dubbo 缓存是 Consumer 端对 RPC 调用结果的本地缓存,适合读多写少、对实时性要求不高的场景。生产中更推荐用 Redis 等分布式缓存。
深度解析
一、缓存工作机制
Dubbo 的缓存作用在 Consumer 端,整个流程很直观:
上图展示了 Dubbo 缓存的完整工作流程。核心逻辑就是在 RPC 调用前加了一层本地缓存拦截:
- 查询阶段:Consumer 发起调用时,先查本地缓存,如果缓存命中,直接返回结果,不再发起远程调用
- 未命中处理:缓存没有命中时,正常发起 RPC 请求到 Provider,拿到结果后写入缓存
- 缓存粒度:缓存的 Key 是方法名 + 参数值,不同参数组合对应不同的缓存条目
注意,这是消费端本地缓存,不是分布式缓存。每个 Consumer JVM 各自维护自己的缓存副本,互相隔离。
二、四种缓存策略详解
1. LRU(默认)
LRU 是 Least Recently Used 的缩写,即 "最近最少使用"。当缓存满了之后,优先淘汰最久没被访问的数据:
<!-- XML 方式配置 LRU 缓存 -->
<dubbo:reference id="userService"
interface="com.example.UserService"
cache="lru" />
// 注解方式配置
@DubboReference(cache = "lru")
private UserService userService;
默认容量是 1000 条,可以通过参数调整:
<dubbo:reference id="userService"
interface="com.example.UserService"
cache="lru">
<dubbo:method name="getUser" cache="lru" />
</dubbo:reference>
LRU 适合大多数场景,因为它能自动淘汰冷数据,不会无限膨胀。
2. ThreadLocal
线程级缓存,缓存在当前线程的 ThreadLocal 中,线程结束就没了:
<dubbo:reference id="userService"
interface="com.example.UserService"
cache="threadlocal" />
这个策略的应用场景比较特殊——比如在一次 HTTP 请求的处理过程中,多个地方需要调用同一个 Dubbo 服务获取同一份数据,用 threadlocal 就能避免同一次请求内的重复 RPC 调用。
但要注意:用完必须清理,否则可能内存泄漏。Dubbo 在实现中会通过 Filter 的 afterInvocation 阶段自动清理,不用太操心。
3. JCache
实现了 JSR-107(Java Caching API)规范,可以对接 Ehcache、Caffeine 等专业缓存框架:
<!-- 需要引入 JCache 实现依赖,如 Ehcache -->
<dubbo:reference id="userService"
interface="com.example.UserService"
cache="jcache" />
适合需要更精细的缓存控制(如 TTL、淘汰策略、持久化等)的场景。说实话,都用到这个级别了,不如直接上 Redis。
4. Expiring
基于时间的过期缓存,可以设置缓存的有效期:
<dubbo:reference id="userService"
interface="com.example.UserService"
cache="expiring" />
适合数据在一段时间内不会变化的场景,比如配置信息、字典数据。
三、生产环境怎么选?
说实话,Dubbo 自带的缓存比较适合轻量级场景,比如:
- 基础数据服务(省份列表、字典数据等),读多写少,变化频率低
- 临时降低对下游服务的压力,做一个快速的短期优化
- 开发/测试环境中减少对远程服务的依赖
不太适合的场景:
- 数据频繁变更的业务(会导致脏读)
- 多 Consumer 实例需要缓存一致性的场景(本地缓存天然不一致)
- 缓存数据量大、需要持久化的场景
大多数生产环境中,真正需要的分布式缓存还是得靠 Redis、Caffeine + TTL 等专业方案,Dubbo 自带缓存更多是一个辅助手段。
面试高频追问
-
追问:Dubbo 缓存会不会导致数据不一致?
会。因为是 Consumer 本地缓存,Provider 数据更新后,Consumer 的缓存不会自动失效。如果对数据一致性有要求,要么不用这个缓存,要么用较短的过期时间,要么改用 Redis 并配合发布/订阅机制来通知失效。
-
追问:缓存是方法级别的还是服务级别的?
可以配置在服务级别(整个服务的所有方法都走缓存),也可以配置在方法级别(只对特定方法启用缓存)。方法级别更精细,推荐按需配置。
-
追问:Dubbo 缓存和 Redis 缓存怎么选?
Dubbo 缓存是 JVM 本地缓存,速度快但不共享;Redis 是分布式缓存,多实例共享但多了一次网络开销。简单读多写少且对一致性不敏感 → Dubbo 缓存;需要共享、需要一致性、数据量大 → Redis。两者也可以组合使用,本地缓存做一级、Redis 做二级。
常见面试变体
- "Dubbo 的
cache属性有哪几个取值?" - "Dubbo 本地缓存和 Redis 缓存的区别?"
- "什么场景下会用 Dubbo 的 ThreadLocal 缓存?"
总结
Dubbo 的缓存是 Consumer 端的轻量级本地缓存,提供 lru、threadlocal、jcache、expiring 四种策略,默认用 LRU。适合读多写少、对实时性要求不高的基础数据服务。生产环境中,真正复杂的缓存需求还是得靠 Redis 等分布式缓存方案,Dubbo 自带缓存更多是一个锦上添花的功能。面试时把四种策略说清楚、再点一下 "本地缓存的局限性",基本就够了。
