📚 ZooKeeper
🌟 特点
- 高可用性 ZooKeeper 可以运行在集群模式下,确保即使部分节点失效,服务仍然可用。
- 分布式协调 提供原语,如分布式锁、配置管理和领导者选举,帮助分布式系统的协调。
- 持久性 数据被存储在磁盘上,即使在重启后,数据也不会丢失。
- 事件监听 客户端可以监听数据节点的变化,从而实现推送机制。
- 简单的API 提供了一个简单而直观的API,用于操作数据。
- 安全性 支持基于 ACL 的安全机制。
🏛️ 结构
- Znode ZooKeeper 的数据模型是一个层次化的命名空间,与文件系统类似。每一个节点称为 Znode。
- Version 每个 Znode 都有一个与之相关的版本号,每次 Znode 更新都会导致其版本号增加。
- Watches 客户端可以在 Znode 上设置 watches,当Znode发生变化时,会通知客户端。
- Ensemble 在高可用配置中运行的 ZooKeeper 实例组。
📘 原理
- 领导者选举 当 ZooKeeper 集群启动或领导者失效时,会进行领导者选举。只有领导者可以进行写操作,但所有服务器都可以进行读操作。
- 同步数据 所有的写操作都会被复制到所有的服务器上,确保数据的一致性。
- 原子性 所有的写操作都是原子的,要么完全成功,要么完全失败。
- 顺序性 所有的请求都是按照 FIFO 顺序进行的。
- 持久性 一旦写入的数据被确认,它会持久存储在服务器上,直到被明确地删除。
⚠️ 注意事项
- 不适合大数据存储 尽管 ZooKeeper 可以存储数据,但它并不适合存储大量的数据。每个 Znode 的数据大小有 1MB 的限制。
- 避免频繁的写操作 频繁的写操作可能会导致领导者的过早失效。
- 持久化的节点 默认情况下,当没有客户端连接到 Znode 时,Znode 将被删除。如果需要持久化的节点,需要特别设置。
- 配置管理 使用 ZooKeeper 进行配置管理时,确保敏感数据加密,避免安全风险。
- 版本管理 每次更新 Znode 时,都需要考虑其版本号,确保数据的一致性。
总的来说,ZooKeeper 是一个强大的分布式协调服务,但使用时需要注意其特性和局限性,确保其正确和高效的使用。
📢 ZooKeeper 功能概览
Apache ZooKeeper 是一个分布式的、开放源码的协调服务,它为大型分布式系统提供了一组简单的原语。这些原语用于在分布式应用中实现诸如同步、配置管理、分布式锁定和命名服务等高级功能。以下是 ZooKeeper 的主要功能概览:
1️⃣ 数据存储与监视
- 当涉及到 Spring Cloud 和微服务架构,服务发现和配置管理是关键组件。ZooKeeper,作为一个协调和配置管理工具,在这里可以发挥其强大的作用。
📝 使用场景
- 在微服务架构中,经常需要外部化配置,并动态地加载或更新这些配置。ZooKeeper 作为一个分布式的配置中心,可以存储这些配置信息,而 Spring Cloud 应用可以作为客户端来监视这些配置。
⚡️ 结合 Spring Cloud
-
外部化配置
- 将服务的配置存储在 ZooKeeper 中的 znodes。
- 使用 Spring Cloud ZooKeeper Config,可以轻松地从 ZooKeeper znodes 加载配置。
-
动态加载配置
- 当在 ZooKeeper 中的 znode 数据发生变化时,Spring Cloud 应用会自动重新加载其配置,无需重启。
- 利用 ZooKeeper 的监视特性,当配置发生变更时,Spring Cloud 应用会收到通知。
-
使用 Spring Cloud ZooKeeper
首先,需要添加 Spring Cloud ZooKeeper 依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-config</artifactId>
</dependency>
然后,配置 bootstrap.yml
:
spring:
cloud:
zookeeper:
connect-string: localhost:2181
config:
root: config
在上述配置中,所有的服务配置都将存储在 /config
路径下的 znodes。
现在,只需在 ZooKeeper 中更新配置信息,Spring Cloud 应用会自动加载新的配置值。
✅ 优势
- 集中管理:所有服务的配置都存储在一个集中的位置,方便管理。
- 动态更新:能够在不中断服务的情况下动态更新配置。
- 版本控制:ZooKeeper 提供了配置的版本控制,可以轻松地回滚到先前的配置。
2️⃣ 分布式锁
- 在微服务的环境中,多个服务实例可能会尝试同时访问共享资源,如数据库或外部系统。这可能会导致数据不一致或其他问题。为了确保只有一个服务实例可以访问资源,可以使用分布式锁。ZooKeeper 提供了这种分布式锁的功能,而 Spring Cloud 可以简化它的使用。
📝 使用场景
- 假设有一个微服务需要访问数据库并更新一个计数器。如果多个服务实例同时尝试更新此计数器,可能会导致数据不一致。为了防止这种情况,可以使用分布式锁确保同一时间只有一个服务实例可以更新计数器。
⚡️ 结合 Spring Cloud
-
创建锁
- 利用 ZooKeeper 创建一个 znode,该 znode 代表锁。
-
请求锁
- 当服务实例需要访问共享资源时,它会尝试创建一个临时 znode。
- 如果创建成功,则该服务实例获得锁并可以访问资源。
- 如果创建失败(因为其他服务实例已经持有锁),则该服务实例必须等待直到锁可用。
-
释放锁
- 一旦服务实例完成对共享资源的访问,它必须删除其临时 znode,从而释放锁。
-
监听锁的释放
- 服务实例可以设置监听器来监听锁 znode,这样,一旦锁被释放,它们可以立即被通知并尝试再次获得锁。
-
使用 Spring Cloud ZooKeeper
首先,确保您已经添加了Spring Cloud ZooKeeper的依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper</artifactId>
</dependency>
下面是如何使用 ZooKeeper 的分布式锁:
- 配置ZooKeeper连接信息
在 application.yml
或 application.properties
中:
spring:
cloud:
zookeeper:
connect-string: localhost:2181
- 注入 ZookeeperLockProvider
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.zookeeper.ZookeeperProperties;
import org.springframework.integration.zookeeper.lock.ZookeeperLockRegistry;
...
@Autowired
private ZookeeperProperties zookeeperProperties;
@Bean
public ZookeeperLockRegistry zookeeperLockRegistry() {
return new ZookeeperLockRegistry(zookeeperProperties.getConnectString());
}
- 使用锁
import org.springframework.integration.support.locks.LockRegistry;
import java.util.concurrent.locks.Lock;
...
@Autowired
private LockRegistry lockRegistry;
public void updateSharedResource() {
Lock lock = lockRegistry.obtain("SOME_LOCK_NAME");
try {
lock.lock();
// 对共享资源进行操作
} finally {
lock.unlock();
}
}
在上述代码中,updateSharedResource
方法使用了一个名为 SOME_LOCK_NAME
的锁来保护共享资源的访问。当一个服务实例持有这个锁时,其他实例必须等待直到这个锁被释放。
这就是在 Spring Cloud 环境中使用 ZooKeeper 进行分布式锁的基本步骤。您可以根据实际需求调整和扩展上述代码。
✅ 优势
- 确保数据一致性:在分布式环境中,可以确保同一时间只有一个服务实例访问共享资源。
- 防止资源冲突:通过使用分布式锁,可以避免资源冲突和潜在的数据不一致问题。
3️⃣ 领导选举
- 领导选举是分布式系统中的关键组件。当一个或多个节点可以作为领导者时,必须有一个机制来确定哪个节点应当承担这一角色。领导者负责协调其他节点的工作,并在其故障时由其他节点接管。ZooKeeper 提供了领导选举的功能,而 Spring Cloud ZooKeeper 可以简化它的使用。
📝 使用场景
- 在一个微服务集群中,可能只需要一个服务实例来执行某些任务,例如数据库清理、报告生成等。通过领导选举,可以确保只有一个服务实例执行这些任务,而其他实例则作为备份,准备在领导者故障时接管任务。
⚡️ 结合 Spring Cloud
- 配置ZooKeeper连接信息
在 application.yml
或 application.properties
中:
spring:
cloud:
zookeeper:
connect-string: localhost:2181
- 使用 LeaderInitiator
import org.springframework.cloud.zookeeper.leader.LeaderInitiator;
...
@Autowired
private ApplicationContext applicationContext;
@Bean
public LeaderInitiator leaderInitiator() {
return new LeaderInitiator(curatorFramework, new DefaultCandidate(applicationContext.getId(), "someRole"));
}
- 监听领导选举事件
import org.springframework.context.event.EventListener;
import org.springframework.cloud.zookeeper.leader.event.OnGrantedEvent;
import org.springframework.cloud.zookeeper.leader.event.OnRevokedEvent;
...
@EventListener(OnGrantedEvent.class)
public void leaderGranted() {
// 当前实例已成为领导者
// 在这里执行领导者的任务
}
@EventListener(OnRevokedEvent.class)
public void leaderRevoked() {
// 当前实例不再是领导者
// 在这里停止领导者的任务
}
✅ 优势
- 确保任务只在一个实例上执行:通过领导选举,可以确保特定的任务只在一个服务实例上执行,从而避免重复或冲突。
- 提供高可用性:如果领导者实例故障,另一个实例会被选为新的领导者,从而确保任务的持续执行。
4️⃣ 配置管理
- ZooKeeper 可以用作中心化的配置管理服务,帮助分布式系统的各个组件保持配置同步。当配置发生更改时,ZooKeeper 可以通知所有连接的客户端,使其能够动态地更新其配置,而无需重新启动。Spring Cloud 提供了与 ZooKeeper 集成的工具,使配置管理变得更加简单。
📝 使用场景
- 微服务架构中的各个服务可能需要共享配置。
- 动态调整系统参数而不需要重启服务。
⚡️ 结合 Spring Cloud
- 添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-config</artifactId>
</dependency>
- 配置ZooKeeper连接信息
在 application.yml
或 application.properties
中:
spring:
cloud:
zookeeper:
connect-string: localhost:2181
config:
enabled: true
- 配置存储
在 ZooKeeper 中,你可以存储配置键值对,例如:
/configuration/applicationName/propertyName = propertyValue
- 读取配置
在 Spring Boot 应用中,可以像普通的配置属性一样访问这些 ZooKeeper 中的配置。
@Value("${propertyName}")
private String propertyValue;
- 监听配置更改
当 ZooKeeper 中的配置更改时,Spring Cloud ZooKeeper 可以自动刷新 Bean。你只需要添加 @RefreshScope
注解到你的 Bean 上。
@RestController
@RefreshScope
public class MyController {
@Value("${propertyName}")
private String propertyValue;
@GetMapping("/property")
public String getProperty() {
return propertyValue;
}
}
✅ 优势
- 中心化管理:所有服务的配置都存储在一个中心位置,使管理和维护变得更加容易。
- 动态更新:当配置更改时,服务可以自动捕获这些更改,无需人工干预或重启。
5️⃣ 命名服务
- ZooKeeper 不仅可以用作配置管理,还可以用作服务发现和注册的命名服务。它允许服务在启动时向 ZooKeeper 注册其可用性,并允许客户端发现和查找这些服务。Spring Cloud 提供了与 ZooKeeper 集成的工具,使服务发现和注册变得更加简单。
📝 使用场景
- 微服务架构中的服务需要动态地发现其他服务的位置。
- 服务的实例数量和位置可能会动态变化。
⚡️ 结合 Spring Cloud
- 添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>
- 配置 ZooKeeper 连接信息
在 application.yml
或 application.properties
中:
spring:
cloud:
zookeeper:
connect-string: localhost:2181
discovery:
enabled: true
- 服务注册
只需启动 Spring Boot 应用,它会自动将自己注册到 ZooKeeper 中。
- 服务发现
可以使用 DiscoveryClient
来查找其他服务:
@Autowired
private DiscoveryClient discoveryClient;
public List<ServiceInstance> getInstances(String serviceName) {
return discoveryClient.getInstances(serviceName);
}
- 使用 Ribbon 进行客户端负载均衡
Ribbon 是一个客户端负载均衡工具,可以与 ZooKeeper 结合使用。你可以为 REST 调用设置一个负载均衡的 RestTemplate
:
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
然后使用这个 RestTemplate
来调用其他服务:
@Autowired
private RestTemplate restTemplate;
public String callService(String serviceName) {
return restTemplate.getForObject("http://" + serviceName + "/endpoint", String.class);
}
✅ 优势
- 动态服务发现:无需手动配置服务的位置。
- 负载均衡:Ribbon 提供了客户端负载均衡,确保请求均匀分配到所有服务实例。
- 自动重新注册:如果服务实例崩溃并重新启动,它会自动重新注册到 ZooKeeper。
6️⃣ 集群成员管理
- ZooKeeper 的集群成员管理功能主要用于跟踪分布式应用或集群中的所有活动节点。它可以帮助应用程序在节点更改(例如节点加入或退出)时获得实时的集群视图。
📝 使用场景
- 微服务架构中的服务需要知道集群中的其他服务实例。
- 当集群中的节点数量或状态发生变化时,需要动态地感知和调整。
⚡️ 结合 Spring Cloud
- 添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>
- 配置 ZooKeeper 连接信息
在 application.yml
或 application.properties
中:
spring:
cloud:
zookeeper:
connect-string: localhost:2181
discovery:
enabled: true
- 监听服务实例更改
可以使用 PathChildrenCache
来监听 ZooKeeper 中的路径变化:
@Autowired
private CuratorFramework curator;
public void watchClusterNodes() throws Exception {
PathChildrenCache childrenCache = new PathChildrenCache(curator, "/path/to/watch", true);
childrenCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
childrenCache.getListenable().addListener((client, event) -> {
switch (event.getType()) {
case CHILD_ADDED:
System.out.println("Node added: " + event.getData().getPath());
break;
case CHILD_REMOVED:
System.out.println("Node removed: " + event.getData().getPath());
break;
case CHILD_UPDATED:
System.out.println("Node updated: " + event.getData().getPath());
break;
default:
break;
}
});
}
✅ 优势
- 实时感知:可以实时获取集群中的所有活动节点,并在节点状态更改时得到通知。
- 灵活性:可以根据需要选择监听特定的路径或节点。
- 高可用性:ZooKeeper 提供的集群成员管理功能是可靠的,即使在部分节点故障的情况下也能正常工作。
7️⃣ 分布式同步
- ZooKeeper 的分布式同步功能使得多个独立的服务或应用实例能够在某些操作或任务上达成一致。这在微服务架构中是非常有用的,特别是当多个服务实例需要在某个时间点同步状态或数据时。
📝 使用场景
- 在分布式系统中,确保只有一个服务实例执行某个任务。
- 在进行数据更新或迁移时,确保所有服务实例都达到了一致的状态。
⚡️ 结合 Spring Cloud
- 添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>
- 使用 InterProcessMutex 实现分布式锁
@Autowired
private CuratorFramework curator;
public void performDistributedTask() throws Exception {
InterProcessMutex mutex = new InterProcessMutex(curator, "/path/to/lock");
if (mutex.acquire(10, TimeUnit.SECONDS)) {
try {
// Perform synchronized operations here
} finally {
mutex.release();
}
}
}
- 使用 Barrier 实现分布式屏障
@Autowired
private CuratorFramework curator;
public void performBarrierOperation() throws Exception {
DistributedBarrier barrier = new DistributedBarrier(curator, "/path/to/barrier");
barrier.setBarrier();
barrier.waitOnBarrier();
// Perform operations after all instances have reached the barrier
}
✅ 优势
- 确保同步:ZooKeeper 提供的原语可以确保在分布式环境中的多个实例之间实现同步。
- 灵活性:ZooKeeper 提供了多种原语,可以满足不同的同步需求。
- 高可靠性:ZooKeeper 的分布式同步功能在网络分区或节点故障的情况下仍然可靠。
8️⃣ 高可用性
- ZooKeeper 的高可用性特性为分布式系统提供了一种可靠的方式来保证服务的持续可用性,即使在面对节点故障时。在微服务架构中,这种能力特别重要,因为它可以确保服务注册、发现和其他关键操作在某些节点出现问题时仍然正常工作。
📝 使用场景
- 服务注册和发现 在微服务架构中,服务可能会频繁地上下线,高可用的服务注册中心可以确保服务列表始终是最新的。
- 配置管理 确保所有的服务实例都能从一个可靠的源获取最新的配置信息。
⚡️ 结合 Spring Cloud
- 添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>
- 配置 ZooKeeper 集群
在 application.yml
或 application.properties
中:
spring:
cloud:
zookeeper:
connect-string: zk1:2181,zk2:2181,zk3:2181
这里,zk1
, zk2
, 和 zk3
是 ZooKeeper 服务器的地址。
- 使用 Spring Cloud 的服务发现功能
通过注入 DiscoveryClient
,你可以查询服务列表,检查服务的健康状态等。
@Autowired
private DiscoveryClient discoveryClient;
public List<String> getAvailableServices() {
return discoveryClient.getServices();
}
✅ 优势
- 故障转移 如果 ZooKeeper 的某个节点失效,客户端请求会自动转移到其他健康的节点上。
- 数据一致性 所有的 ZooKeeper 服务器都保存有相同的数据副本,确保了数据的一致性。
- 动态扩展 可以在不中断服务的情况下,增加或删除 ZooKeeper 服务器节点。
9️⃣ 简单的API
- ZooKeeper 提供的简单 API 允许开发人员快速地集成并利用其功能,特别是在微服务架构中。Spring Cloud 已经为 ZooKeeper 提供了高级的抽象,但了解其底层 API 仍然是有用的,因为它可以提供更多的灵活性和控制。
📝 使用场景
- 自定义服务注册与发现 虽然 Spring Cloud 提供了自己的服务注册和发现机制,但有时可能需要更精细的控制,例如,自定义的健康检查逻辑或元数据。
- 复杂的分布式协调 对于需要复杂的协调逻辑,如选举、分布式锁等,可以直接使用 ZooKeeper 的原始 API。
⚡️ 结合 Spring Cloud
- 添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>
- 使用 ZooKeeper 客户端
可以直接使用 CuratorFramework
,这是 Apache Curator 项目为 ZooKeeper 提供的客户端库,它进一步简化了 ZooKeeper 的原始 API。
@Autowired
private CuratorFramework client;
public void createZNode(String path, byte[] data) throws Exception {
client.create().forPath(path, data);
}
public byte[] getZNodeData(String path) throws Exception {
return client.getData().forPath(path);
}
- 高级功能
使用 Curator 也可以实现分布式锁、领导选举等高级功能。
public void distributedLock(String lockPath) throws Exception {
InterProcessMutex lock = new InterProcessMutex(client, lockPath);
try {
if (lock.acquire(10, TimeUnit.SECONDS)) {
// do critical section code
}
} finally {
lock.release();
}
}
✅ 优势
- 灵活性 能够直接使用 ZooKeeper API 提供了更多的控制和灵活性。
- 丰富的功能 除了基本的数据存储和监视功能外,可以实现复杂的分布式协调任务。
🔟 安全性
- ZooKeeper 提供了基于 ACL 的安全性,确保数据的访问和修改只能由授权的客户端进行。Spring Cloud 与 ZooKeeper 整合时,可以利用这些安全特性来确保微服务间的交互是安全的。
📝 使用场景
- 服务注册与发现 当使用 ZooKeeper 作为服务注册中心时,可以确保只有授权的服务能够注册,其他未授权的服务或攻击者无法篡改服务列表。
- 配置管理 存储在 ZooKeeper 中的配置信息可以被设置为只读,确保只有授权的应用或服务能够修改。
⚡️ 结合 Spring Cloud
- 添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>
- 配置 ZooKeeper 安全性
在 application.properties
或 application.yml
中配置 ZooKeeper 客户端的 ACL 信息:
spring:
cloud:
zookeeper:
connect-string: localhost:2181
acl: auth:user:password:cdrwa
上述配置中的 acl
表示使用 auth
方式进行身份验证,用户名为 user
,密码为 password
,并且具有创建、删除、读取、写入和管理的权限(cdrwa
)。
- 使用 ZooKeeper 客户端
当使用 CuratorFramework
进行操作时,Spring Cloud 会自动应用配置的 ACL 信息,确保操作是安全的。
@Autowired
private CuratorFramework client;
public void createZNodeWithACL(String path, byte[] data) throws Exception {
// 使用前面配置的 ACL 创建 znode
client.create().withMode(CreateMode.PERSISTENT).forPath(path, data);
}
✅ 优势
- 访问控制 通过 ACL,可以细粒度地控制哪些客户端或服务可以进行哪些操作。
- 防止未授权访问 未授权的客户端无法访问或修改 ZooKeeper 中的数据。