Nacos 中使用了哪些缓存?缓存的目的是什么?是如何实现的?

在这里插入图片描述

Nacos 在服务端和客户端都广泛的使用了缓存机制,下面着重介绍一下。

一、 Nacos 服务端缓存 (Server-Side Caching)

Nacos 服务端缓存的主要目的是提高读取性能、降低对底层存储(数据库或磁盘文件)的压力,并加速对客户端请求的响应

  1. 缓存的内容:

    • 服务实例信息: 注册到 Nacos 的服务实例列表(IP、端口、权重、元数据、健康状态等)。这是服务发现的核心数据,查询频率非常高。
    • 服务信息: 服务的元数据,例如服务名、分组名、保护阈值等。
    • 配置信息: 用户发布的配置内容(按 dataIdgroup 区分)。客户端会频繁拉取或监听配置变更。
    • 集群节点信息: Nacos 集群自身节点的状态和信息。
    • 其他内部状态或元数据: 用于加速内部处理逻辑。
  2. 缓存的目的:

    • 提升读性能/降低延迟: 从内存缓存中读取数据远快于从数据库或磁盘读取,可以极大加速客户端对服务列表或配置内容的查询请求。
    • 减轻存储层负载: 大量的客户端(可能成千上万)会频繁请求服务实例和配置信息。如果每次请求都直接访问数据库,会对数据库造成巨大压力,容易成为性能瓶颈。缓存层可以挡住绝大部分读请求。
    • 提高可用性: 即使底层存储暂时出现抖动,只要缓存中有数据,服务端仍然可以在一定程度上继续提供(可能是稍微过期的)服务发现和配置查询能力。
  3. 实现方式:

    • 内存缓存: 主要使用 JVM 内存作为缓存存储。常见使用 java.util.concurrent.ConcurrentHashMap 等线程安全的 Map 结构来存储缓存数据,Key 通常是服务名、dataId+group 的组合等。
    • 缓存更新机制:
      • 写操作触发更新/失效: 当有数据变更操作时(如服务注册、注销、心跳更新、配置发布/删除),Nacos 首先会更新底层持久化存储(如数据库)。操作成功后,会主动更新或失效相应的内存缓存。这是保证缓存一致性的关键。
      • 事件驱动: Nacos 内部通常有事件发布/订阅机制。当数据发生变化并持久化成功后,会发布一个事件,相关的缓存监听器接收到事件后更新缓存。
      • 一致性协议关联: 在集群模式下,数据的变更通常通过一致性协议(如 Raft 或 Nacos 早期版本的类 Distro 协议)同步到其他节点。当一个节点通过协议成功应用(Apply)了一个数据变更日志后,它会更新自己的内存缓存。这保证了集群中各个节点缓存状态的最终一致性。
      • 懒加载/定时加载 (较少用于核心数据): 某些非核心或不经常变动的数据可能会在首次读取时加载,或通过定时任务定期从存储刷新。但对于服务实例和配置这类核心数据,主动更新是主要方式。
    • 分层缓存 (可能): 某些实现可能会考虑使用多级缓存(如 L1/L2),但 Nacos 的核心缓存主要是基于内存的单层缓存。

二、 Nacos 客户端缓存 (Client-Side Caching)

Nacos 客户端(即你的应用程序中引入 nacos-client 的部分)也实现了缓存,主要目的是提高应用程序性能、降低对 Nacos Server 的请求频率,并在 Nacos Server 短暂不可用时提供一定的容错能力

  1. 缓存的内容:

    • 服务实例列表: 客户端订阅的服务,其可用的实例信息会被缓存在客户端内存中。
    • 配置信息: 客户端获取到的配置内容会被缓存在内存中,并且通常会有一个本地文件快照(Failover 机制)。
  2. 缓存的目的:

    • 提升应用性能: 应用获取服务实例或配置时,可以直接从本地内存缓存读取,避免了网络请求的开销,速度极快。
    • 降低服务端压力: 客户端不需要每次需要数据都去请求 Nacos Server,大大减少了服务端的 QPS。
    • 提高应用容错性/可用性 (Failover):
      • 服务发现: 如果 Nacos Server 暂时宕机或网络不通,客户端仍然可以使用本地缓存的(可能是稍微过期的)服务实例列表进行服务调用,避免完全瘫痪。
      • 配置管理: 如果 Nacos Server 不可用,客户端可以加载本地磁盘上的配置快照文件 (snapshot 目录),保证应用能够使用上一份正确的配置启动或运行。
  3. 实现方式:

    • 内存缓存: 同样主要使用 ConcurrentHashMap 等内存结构存储服务实例和配置。
    • 本地文件快照 (配置管理): Nacos Client 会将成功获取到的配置内容写入到本地磁盘的一个文件中(通常在用户主目录下的 nacos/config 目录的 snapshot 子目录)。当启动时无法连接 Nacos Server 或获取配置失败时,会尝试从这个快照文件加载。
    • 缓存更新机制:
      • 服务发现:
        • 定时拉取: 客户端会定时(可配置)向 Nacos Server 拉取最新的服务实例列表来更新本地缓存。
        • UDP推送 (Nacos 1.x): Nacos 1.x 版本支持服务端通过 UDP 协议主动将变更推送给客户端(但 UDP 不可靠,仍需定时拉取保证)。
        • gRPC 长连接/Watch (Nacos 2.x): Nacos 2.x 推荐使用 gRPC,客户端与服务端建立长连接。当服务实例发生变化时,服务端可以通过长连接主动将变更推送给客户端,实现更实时的更新。即使有推送,通常也会保留定时拉取作为兜底。
      • 配置管理:
        • 长轮询 (Long Polling): 客户端向服务端发起一次获取配置的请求。如果配置没有变更,服务端会 hold 住这个连接一段时间(例如 30 秒)。如果期间配置发生变更,服务端会立即返回变更内容;如果超时仍无变更,服务端返回无变更,客户端收到响应后立即发起下一次长轮询。这种方式可以准实时地获取配置变更,同时避免了大量无效的轮询请求。
        • gRPC 长连接/Watch (Nacos 2.x): 类似服务发现,通过 gRPC 长连接实现配置变更的主动推送。
    • 缓存失效: 客户端缓存通常没有复杂的过期策略,主要依赖于服务端的推送或客户端的定时/长轮询来更新。

总结:

Nacos 中的缓存是其架构设计的核心部分,遍布服务端和客户端。

  • 服务端缓存 主要为了 提升性能和降低存储压力,通过内存缓存和主动更新机制实现。
  • 客户端缓存 主要为了 提升应用性能、降低服务端压力和提供故障容错能力,通过内存缓存、本地文件快照以及与服务端的实时/准实时同步机制(如 gRPC Watch、长轮询)实现。
### 集成Nacos到Java项目 #### 添加依赖项 为了使 Java 项目能够利用 Nacos 进行配置管理和服务发现,需在项目的 `pom.xml` 文件中加入如下 Maven 依赖: ```xml <dependency> <groupId>com.alibaba.nacos</groupId> <artifactId>nacos-client</artifactId> <version>2.1.1</version> </dependency> ``` 此操作允许应用程序通过客户端库与 Nacos Server 进行交互[^1]。 #### 初始化Nacos Client实例 创建并初始化用于访问 Nacos 的客户端对象是必要的一步。这通常涉及设置服务器地址和其他连接参数。下面是一个简单的例子展示如何完成这一过程: ```java Properties properties = new Properties(); properties.put("serverAddr", "localhost:8848"); // 设置Nacos server地址 NamingService namingService = NamingFactory.createNamingService(properties); ConfigService configService = NacosFactory.createConfigService(properties); ``` 这段代码片段展示了如何获取命名服务 (`NamingService`) 和配置服务 (`ConfigService`) 的实例,它们分别是用来实现服务注册/发现以及动态配置管理的关键接口。 #### 注册服务与健康检查 为了让其他微服务能发现自己,应用应该向 Nacos 注册自己作为一个提供者,并定期报告其状态给 Nacos 来保持在线记录。这里有一个简单的方法来执行这些动作: ```java try { namingService.registerInstance("example-service-name", "192.168.0.1", 8080, null); // 注册服务实例 } catch (Exception e) { System.out.println(e.getMessage()); } // 假设每30秒发送一次心跳包以维持存活状态 ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); scheduler.scheduleAtFixedRate(() -> { try { namingService.sendHeartbeat("example-service-name", "192.168.0.1", 8080, null); } catch (Exception ex) { System.err.println(ex.getMessage()); } }, 0L, 30L, TimeUnit.SECONDS); ``` 上述代码实现了服务的自我注册及其持续的心跳检测机制,确保该服务始终被识别为活跃成员之一。 #### 获取远程配置 除了作为服务发现工具外,Nacos 同样支持集中式的配置管理功能。开发者可以通过编程的方式轻松读取来自远端存储器中的属性值: ```java String dataId = "example"; String group = "DEFAULT_GROUP"; try { String content = configService.getConfig(dataId, group, 5000); System.out.printf("Fetched configuration from Nacos:%s%n", content); } catch (Exception e) { System.out.println(e.getMessage()); } // 当监听到配置变化时触发回调函数更新本地缓存或重新加载配置 configService.addListener(dataId, group, listener -> { try { String updatedContent = configService.getConfig(dataId, group, 5000); System.out.printf("Configuration has been changed to %s%n", updatedContent); } catch (Exception exception) { System.out.println(exception.getMessage()); } }); ``` 以上示例说明了怎样从 Nacos 中检索特定的数据集,并设置了监听器以便于当外部修改发生时及时响应[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰糖心书房

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值