在 RPC 调用中,服务地址的获取是高频操作。如果每次调用都直接请求注册中心(如 ZooKeeper),不仅会增加注册中心的压力,还会因网络延迟影响调用效率。本地缓存正是解决这一问题的关键 —— 通过在客户端缓存服务地址,减少对注册中心的依赖,同时借助监听机制保证缓存与服务的一致性。
一、本地缓存的实现:客户端的 “地址速查表”
本地缓存的核心思路是:在 RPC 客户端维护一份服务地址的本地副本,调用服务时优先从缓存中获取地址,只有缓存未命中时才请求注册中心。
实现要点:
- 缓存载体:通常用哈希表(如 Java 的
ConcurrentHashMap
)存储,键为服务名,值为该服务的可用地址列表(如Map<String, List<InetSocketAddress>>
)。 - 初始化加载:客户端启动时,从注册中心拉取所有服务的地址列表,初始化本地缓存。
- 查询逻辑:调用服务时,直接从缓存中获取地址(结合负载均衡算法选择一个地址),避免网络请求。
// 简化的本地缓存实现
public class LocalCache {
// 服务名 -> 地址列表
private final ConcurrentHashMap<String, List<InetSocketAddress>> serviceAddresses = new ConcurrentHashMap<>();
// 从缓存获取服务地址
public List<InetSocketAddress> getAddresses(String serviceName) {
return serviceAddresses.get(serviceName);
}
// 初始化缓存(从注册中心拉取)
public void initFromRegistry(Registry registry) {
Map<String, List<InetSocketAddress>> allServices = registry.getAllServices();
serviceAddresses.putAll(allServices);
}
// 更新缓存
public void update(String serviceName, List<InetSocketAddress> newAddresses) {
serviceAddresses.put(serviceName, newAddresses);
}
}
二、缓存与服务的一致性:如何避免 “脏数据”?
本地缓存的最大挑战是 “缓存与注册中心的一致性”—— 如果服务地址发生变化(如节点上线、下线),本地缓存必须及时更新,否则会导致 RPC 调用失败(请求到无效地址)。
关键方案:基于 ZooKeeper 的监听机制
利用 ZooKeeper 的 Watcher 机制,可实现地址变化的实时感知:
- 注册监听:客户端初始化时,对注册中心中服务对应的节点(如
/services/userService
)注册 Watcher。 - 触发通知:当服务地址变化(如节点新增 / 删除),ZooKeeper 会异步通知客户端的 Watcher。
- 更新缓存:客户端收到通知后,重新从注册中心拉取最新地址,更新本地缓存。
三、核心价值与总结
本地缓存在 RPC 中的作用可概括为 “提速 + 减压”:
- 减少对注册中心的请求次数,降低其负载;
- 避免每次调用的网络延迟,提升 RPC 调用效率。
而 ZooKeeper 的监听机制则是一致性的保障 —— 通过 “主动通知 + 被动更新” 的方式,既保证了缓存的实时性,又避免了客户端轮询带来的资源浪费。
记住这一设计:本地缓存做 “速查”,监听机制保 “一致”,二者结合是 RPC 客户端高性能与高可用的基础。