Zookeeper从入门到实战:SpringBoot整合完整指南

一、Zookeeper概述

1.1 什么是Zookeeper

Zookeeper是一个开源的分布式协调服务,由Apache软件基金会维护。它最初是为Hadoop生态系统设计的,但现在已被广泛应用于各种分布式系统中。Zookeeper提供了一种简单而健壮的方式,用于管理分布式环境中的配置信息、命名服务、分布式同步和组服务等。

1.2 Zookeeper的核心特性

  • 顺序一致性:客户端的更新请求将按照它们被发送的顺序进行应用
  • 原子性:更新操作要么成功要么失败,没有中间状态
  • 单一系统映像:无论客户端连接到哪个服务器,都将看到相同的服务视图
  • 可靠性:一旦更新被应用,它将从那时起保持,直到客户端覆盖更新
  • 及时性:系统的客户端视图保证在一定时间范围内是最新的

1.3 Zookeeper的典型应用场景

  1. 配置管理:集中式配置管理
  2. 分布式锁:实现跨JVM的互斥机制
  3. 集群管理:监控集群节点状态
  4. 命名服务:类似DNS的服务
  5. 分布式队列:简单的队列实现
  6. Leader选举:分布式系统中的主节点选举

二、Zookeeper安装与配置

2.1 单机模式安装

下载Zookeeper

wget https://downloads.apache.org/zookeeper/zookeeper-3.7.0/apache-zookeeper-3.7.0-bin.tar.gz
tar -zxvf apache-zookeeper-3.7.0-bin.tar.gz
cd apache-zookeeper-3.7.0-bin

配置Zookeeper

创建配置文件conf/zoo.cfg

# 基本时间单位(毫秒)
tickTime=2000
# 数据目录
dataDir=/tmp/zookeeper
# 客户端连接端口
clientPort=2181
# 初始化连接时能容忍的最长心跳间隔(tickTime的倍数)
initLimit=10
# 发送请求和接收响应能容忍的最长心跳间隔
syncLimit=5

启动Zookeeper

bin/zkServer.sh start

验证安装

bin/zkCli.sh -server 127.0.0.1:2181

2.2 集群模式安装

对于生产环境,建议至少部署3个节点的Zookeeper集群。修改每个节点的zoo.cfg

tickTime=2000
dataDir=/var/lib/zookeeper
clientPort=2181
initLimit=10
syncLimit=5
# 集群配置 server.id=host:port1:port2
server.1=node1:2888:3888
server.2=node2:2888:3888
server.3=node3:2888:3888

在每个节点的dataDir目录下创建myid文件,内容为对应的server.id中的id:

# 在node1上
echo 1 > /var/lib/zookeeper/myid # 在node2上
echo 2 > /var/lib/zookeeper/myid # 在node3上
echo 3 > /var/lib/zookeeper/myid

三、Zookeeper基础操作

3.1 基本命令操作

通过zkCli.sh连接后可以执行以下命令:

# 查看根节点
ls / # 创建持久节点
create /myapp "myapp data" # 创建临时节点(会话结束自动删除)
create -e /myapp/tempnode "temp data" # 创建顺序节点
create -s /myapp/seqnode "seq data" # 获取节点数据
get /myapp # 设置节点数据
set /myapp "new data" # 删除节点
delete /myapp/seqnode0000000001 # 递归删除节点
rmr /myapp # 查看节点状态
stat /myapp

3.2 Zookeeper节点类型

  1. 持久节点(PERSISTENT):创建后一直存在,除非显式删除
  2. 临时节点(EPHEMERAL):客户端会话结束时自动删除
  3. 持久顺序节点(PERSISTENT_SEQUENTIAL):持久节点,但节点名后会附加一个单调递增的数字后缀
  4. 临时顺序节点(EPHEMERAL_SEQUENTIAL):临时节点,带有序号后缀

3.3 Zookeeper Watch机制

Watch是Zookeeper的一个重要特性,它允许客户端在节点发生变化时收到通知。

# 设置watch
get /myapp watch # 另一个会话修改/myapp节点数据
set /myapp "changed data" # 原会话会收到NodeDataChanged事件

四、Zookeeper Java客户端API

4.1 原生Java客户端

首先添加Maven依赖:

<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.7.0</version>
</dependency>

基本操作示例

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat; import java.io.IOException;
import java.util.concurrent.CountDownLatch; public class ZookeeperDemo {
private static final String CONNECT_STRING = "localhost:2181";
private static final int SESSION_TIMEOUT = 5000;
private static ZooKeeper zk;
private static final CountDownLatch connectedSemaphore = new CountDownLatch(1); public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
// 创建连接
zk = new ZooKeeper(CONNECT_STRING, SESSION_TIMEOUT, new Watcher() {
@Override
public void process(WatchedEvent event) {
if (Event.KeeperState.SyncConnected == event.getState()) {
connectedSemaphore.countDown();
}
}
});
connectedSemaphore.await();
System.out.println("Zookeeper连接成功"); // 创建节点
String path = "/test-node";
String data = "test data";
zk.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println("创建节点: " + path); // 获取节点数据
byte[] nodeData = zk.getData(path, false, null);
System.out.println("节点数据: " + new String(nodeData)); // 修改节点数据
String newData = "new test data";
zk.setData(path, newData.getBytes(), -1);
System.out.println("修改节点数据"); // 删除节点
zk.delete(path, -1);
System.out.println("删除节点: " + path); // 关闭连接
zk.close();
}
}

Watch示例

public class ZookeeperWatchDemo {
// ... 同上连接代码 public static void watchDemo() throws KeeperException, InterruptedException {
String path = "/watch-node"; // 创建节点
zk.create(path, "init".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // 设置watch
Stat stat = new Stat();
byte[] data = zk.getData(path, new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println("收到事件: " + event);
if (event.getType() == Event.EventType.NodeDataChanged) {
try {
// 再次设置watch,实现持续监听
byte[] newData = zk.getData(path, this, null);
System.out.println("数据已修改,新值为: " + new String(newData));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}, stat); System.out.println("初始数据: " + new String(data)); // 修改数据触发watch
zk.setData(path, "changed".getBytes(), stat.getVersion()); // 等待watch触发
Thread.sleep(1000); // 删除节点
zk.delete(path, -1);
}
}

4.2 Curator客户端

Curator是Netflix开源的Zookeeper客户端,提供了更高级的API和常用模式的实现。

添加依赖

<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.2.0</version>
</dependency>

基本操作示例

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat; public class CuratorDemo {
private static final String CONNECT_STRING = "localhost:2181";
private static final int SESSION_TIMEOUT = 5000;
private static final int CONNECTION_TIMEOUT = 3000; public static void main(String[] args) throws Exception {
// 创建客户端
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString(CONNECT_STRING)
.sessionTimeoutMs(SESSION_TIMEOUT)
.connectionTimeoutMs(CONNECTION_TIMEOUT)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build(); // 启动客户端
client.start(); // 创建节点
String path = "/curator-node";
client.create()
.creatingParentsIfNeeded()
.withMode(CreateMode.PERSISTENT)
.forPath(path, "init".getBytes());
System.out.println("创建节点: " + path); // 获取节点数据
byte[] data = client.getData().forPath(path);
System.out.println("节点数据: " + new String(data)); // 修改节点数据
Stat stat = client.setData().forPath(path, "changed".getBytes());
System.out.println("修改节点数据,版本号: " + stat.getVersion()); // 删除节点
client.delete()
.guaranteed()
.deletingChildrenIfNeeded()
.forPath(path);
System.out.println("删除节点: " + path); // 关闭客户端
client.close();
}
}

五、SpringBoot整合Zookeeper

5.1 项目搭建

创建SpringBoot项目并添加依赖:

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.2.0</version>
</dependency> <dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

5.2 配置类

创建Zookeeper配置类:

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; @Configuration
public class ZookeeperConfig { @Value("${zookeeper.connect-string}")
private String connectString; @Value("${zookeeper.session-timeout}")
private int sessionTimeout; @Value("${zookeeper.connection-timeout}")
private int connectionTimeout; @Bean(initMethod = "start", destroyMethod = "close")
public CuratorFramework curatorFramework() {
return CuratorFrameworkFactory.builder()
.connectString(connectString)
.sessionTimeoutMs(sessionTimeout)
.connectionTimeoutMs(connectionTimeout)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.namespace("springboot-demo") // 命名空间隔离
.build();
}
}

application.properties中添加配置:

# Zookeeper配置
zookeeper.connect-string=localhost:2181
zookeeper.session-timeout=5000
zookeeper.connection-timeout=3000

5.3 服务类

创建Zookeeper操作服务类:

import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.CuratorCache;
import org.apache.curator.framework.recipes.cache.CuratorCacheListener;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import java.nio.charset.StandardCharsets;
import java.util.List; @Slf4j
@Service
public class ZookeeperService { @Autowired
private CuratorFramework curatorFramework; /**
* 创建节点
*/
public String createNode(String path, String data, CreateMode createMode) throws Exception {
byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);
String createdPath = curatorFramework.create()
.creatingParentsIfNeeded()
.withMode(createMode)
.forPath(path, dataBytes);
log.info("节点创建成功: {}", createdPath);
return createdPath;
} /**
* 获取节点数据
*/
public String getNodeData(String path) throws Exception {
byte[] dataBytes = curatorFramework.getData().forPath(path);
return new String(dataBytes, StandardCharsets.UTF_8);
} /**
* 更新节点数据
*/
public Stat updateNodeData(String path, String data) throws Exception {
byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);
return curatorFramework.setData().forPath(path, dataBytes);
} /**
* 删除节点
*/
public void deleteNode(String path) throws Exception {
curatorFramework.delete()
.guaranteed()
.deletingChildrenIfNeeded()
.forPath(path);
log.info("节点删除成功: {}", path);
} /**
* 检查节点是否存在
*/
public boolean isNodeExist(String path) throws Exception {
Stat stat = curatorFramework.checkExists().forPath(path);
return stat != null;
} /**
* 获取子节点列表
*/
public List<String> getChildren(String path) throws Exception {
return curatorFramework.getChildren().forPath(path);
} /**
* 添加节点监听
*/
public void addNodeListener(String path, NodeListenerCallback callback) {
CuratorCache cache = CuratorCache.build(curatorFramework, path); CuratorCacheListener listener = CuratorCacheListener.builder()
.forCreates(node -> callback.onNodeCreated(node.getPath(), new String(node.getData())))
.forChanges((oldNode, node) ->
callback.onNodeUpdated(node.getPath(), new String(node.getData())))
.forDeletes(node -> callback.onNodeDeleted(node.getPath()))
.forInitialized(() -> callback.onInitialized()))
.build(); cache.listenable().addListener(listener);
cache.start();
} public interface NodeListenerCallback {
default void onNodeCreated(String path, String data) {}
default void onNodeUpdated(String path, String data) {}
default void onNodeDeleted(String path) {}
default void onInitialized() {}
}
}

5.4 控制器类

创建REST控制器:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; @RestController
@RequestMapping("/zk")
public class ZookeeperController { @Autowired
private ZookeeperService zookeeperService; @PostMapping("/node")
public String createNode(@RequestParam String path,
@RequestParam String data,
@RequestParam(defaultValue = "PERSISTENT") String mode) throws Exception {
return zookeeperService.createNode(path, data, CreateMode.valueOf(mode));
} @GetMapping("/node")
public String getNodeData(@RequestParam String path) throws Exception {
return zookeeperService.getNodeData(path);
} @PutMapping("/node")
public String updateNodeData(@RequestParam String path,
@RequestParam String data) throws Exception {
zookeeperService.updateNodeData(path, data);
return "更新成功";
} @DeleteMapping("/node")
public String deleteNode(@RequestParam String path) throws Exception {
zookeeperService.deleteNode(path);
return "删除成功";
} @GetMapping("/node/exists")
public boolean isNodeExist(@RequestParam String path) throws Exception {
return zookeeperService.isNodeExist(path);
} @GetMapping("/node/children")
public List<String> getChildren(@RequestParam String path) throws Exception {
return zookeeperService.getChildren(path);
}
}

六、Zookeeper高级应用

6.1 分布式锁实现

import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import java.util.concurrent.TimeUnit; @Slf4j
@Service
public class DistributedLockService { private static final String LOCK_PATH = "/locks"; @Autowired
private CuratorFramework curatorFramework; /**
* 获取分布式锁
*/
public boolean acquireLock(String lockName, long waitTime, TimeUnit timeUnit) {
InterProcessMutex lock = new InterProcessMutex(curatorFramework, LOCK_PATH + "/" + lockName);
try {
return lock.acquire(waitTime, timeUnit);
} catch (Exception e) {
log.error("获取分布式锁失败", e);
return false;
}
} /**
* 释放分布式锁
*/
public void releaseLock(String lockName) {
InterProcessMutex lock = new InterProcessMutex(curatorFramework, LOCK_PATH + "/" + lockName);
try {
if (lock.isAcquiredInThisProcess()) {
lock.release();
}
} catch (Exception e) {
log.error("释放分布式锁失败", e);
}
} /**
* 执行带锁的操作
*/
public <T> T executeWithLock(String lockName, long waitTime, TimeUnit timeUnit, LockOperation<T> operation) throws Exception {
InterProcessMutex lock = new InterProcessMutex(curatorFramework, LOCK_PATH + "/" + lockName);
try {
if (lock.acquire(waitTime, timeUnit)) {
return operation.execute();
} else {
throw new RuntimeException("获取锁超时");
}
} finally {
if (lock.isAcquiredInThisProcess()) {
lock.release();
}
}
} public interface LockOperation<T> {
T execute() throws Exception;
}
}

6.2 配置中心实现

import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.CuratorCache;
import org.apache.curator.framework.recipes.cache.CuratorCacheListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; @Slf4j
@Service
public class ConfigCenterService { private static final String CONFIG_PATH = "/config";
private final Map<String, String> configCache = new ConcurrentHashMap<>(); @Autowired
private CuratorFramework curatorFramework; @PostConstruct
public void init() throws Exception {
// 初始化配置
loadAllConfigs(); // 监听配置变化
watchConfigChanges();
} /**
* 加载所有配置
*/
private void loadAllConfigs() throws Exception {
if (curatorFramework.checkExists().forPath(CONFIG_PATH) == null) {
curatorFramework.create().creatingParentsIfNeeded().forPath(CONFIG_PATH);
} List<String> children = curatorFramework.getChildren().forPath(CONFIG_PATH);
for (String child : children) {
String path = CONFIG_PATH + "/" + child;
byte[] data = curatorFramework.getData().forPath(path);
configCache.put(child, new String(data, StandardCharsets.UTF_8));
}
} /**
* 监听配置变化
*/
private void watchConfigChanges() {
CuratorCache cache = CuratorCache.build(curatorFramework, CONFIG_PATH); CuratorCacheListener listener = CuratorCacheListener.builder()
.forCreates(node -> {
String key = node.getPath().replace(CONFIG_PATH + "/", "");
configCache.put(key, new String(node.getData()));
log.info("配置新增: {}={}", key, configCache.get(key));
})
.forChanges((oldNode, node) -> {
String key = node.getPath().replace(CONFIG_PATH + "/", "");
configCache.put(key, new String(node.getData()));
log.info("配置修改: {}={}", key, configCache.get(key));
})
.forDeletes(node -> {
String key = node.getPath().replace(CONFIG_PATH + "/", "");
configCache.remove(key);
log.info("配置删除: {}", key);
})
.build(); cache.listenable().addListener(listener);
cache.start();
} /**
* 获取配置
*/
public String getConfig(String key) {
return configCache.get(key);
} /**
* 获取所有配置
*/
public Map<String, String> getAllConfigs() {
return new HashMap<>(configCache);
} /**
* 设置配置
*/
public void setConfig(String key, String value) throws Exception {
String path = CONFIG_PATH + "/" + key;
byte[] data = value.getBytes(StandardCharsets.UTF_8); if (curatorFramework.checkExists().forPath(path) == null) {
curatorFramework.create().forPath(path, data);
} else {
curatorFramework.setData().forPath(path, data);
}
} /**
* 删除配置
*/
public void deleteConfig(String key) throws Exception {
String path = CONFIG_PATH + "/" + key;
curatorFramework.delete().forPath(path);
}
}

6.3 服务注册与发现

服务注册

import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.zookeeper.CreateMode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import javax.annotation.PostConstruct;
import java.net.InetAddress; @Slf4j
@Service
public class ServiceRegistry { private static final String REGISTRY_PATH = "/services"; @Autowired
private CuratorFramework curatorFramework; @Value("${server.port}")
private int port; private String servicePath; @PostConstruct
public void register() throws Exception {
// 创建服务根节点(持久节点)
if (curatorFramework.checkExists().forPath(REGISTRY_PATH) == null) {
curatorFramework.create()
.creatingParentsIfNeeded()
.forPath(REGISTRY_PATH, "Service Registry".getBytes());
} // 获取本机IP地址
String ip = InetAddress.getLocalHost().getHostAddress();
String serviceInstance = ip + ":" + port; // 创建临时顺序节点
servicePath = curatorFramework.create()
.withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
.forPath(REGISTRY_PATH + "/instance-", serviceInstance.getBytes()); log.info("服务注册成功: {}", servicePath);
} public String getServicePath() {
return servicePath;
}
}

服务发现

import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.CuratorCache;
import org.apache.curator.framework.recipes.cache.CuratorCacheListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List; @Slf4j
@Service
public class ServiceDiscovery { private static final String REGISTRY_PATH = "/services";
private final List<String> serviceInstances = new ArrayList<>(); @Autowired
private CuratorFramework curatorFramework; @PostConstruct
public void init() throws Exception {
// 初始化服务列表
discoverServices(); // 监听服务变化
watchServices();
} /**
* 发现可用服务
*/
private void discoverServices() throws Exception {
serviceInstances.clear(); if (curatorFramework.checkExists().forPath(REGISTRY_PATH) != null) {
List<String> instances = curatorFramework.getChildren().forPath(REGISTRY_PATH);
for (String instance : instances) {
String instancePath = REGISTRY_PATH + "/" + instance;
byte[] data = curatorFramework.getData().forPath(instancePath);
serviceInstances.add(new String(data));
}
} log.info("当前可用服务实例: {}", serviceInstances);
} /**
* 监听服务变化
*/
private void watchServices() {
CuratorCache cache = CuratorCache.build(curatorFramework, REGISTRY_PATH); CuratorCacheListener listener = CuratorCacheListener.builder()
.forCreates(node -> {
try {
discoverServices();
} catch (Exception e) {
log.error("处理服务新增事件失败", e);
}
})
.forChanges((oldNode, node) -> {
try {
discoverServices();
} catch (Exception e) {
log.error("处理服务变更事件失败", e);
}
})
.forDeletes(node -> {
try {
discoverServices();
} catch (Exception e) {
log.error("处理服务删除事件失败", e);
}
})
.build(); cache.listenable().addListener(listener);
cache.start();
} /**
* 获取所有服务实例
*/
public List<String> getAllServiceInstances() {
return new ArrayList<>(serviceInstances);
} /**
* 随机获取一个服务实例(简单的负载均衡)
*/
public String getRandomServiceInstance() {
if (serviceInstances.isEmpty()) {
return null;
}
int index = (int) (Math.random() * serviceInstances.size());
return serviceInstances.get(index);
}
}

七、生产环境注意事项

7.1 Zookeeper性能优化

  1. 数据目录和事务日志目录分离:将dataDir和dataLogDir配置到不同的物理磁盘
  2. JVM调优:适当增加JVM堆内存,设置合适的GC参数
  3. 快照清理:配置autopurge.snapRetainCount和autopurge.purgeInterval自动清理旧快照
  4. 限制客户端连接数:合理设置maxClientCnxns参数

7.2 监控与运维

  1. 使用四字命令监控

    • echo stat | nc localhost 2181 查看服务器状态
    • echo mntr | nc localhost 2181 查看监控信息
    • echo cons | nc localhost 2181 查看客户端连接
  2. JMX监控:启用JMX监控Zookeeper运行状态

  3. 日志管理:合理配置日志级别和日志滚动策略

7.3 常见问题解决

  1. 连接问题

    • 检查防火墙设置
    • 确认Zookeeper服务是否正常运行
    • 检查客户端和服务端版本是否兼容
  2. 节点已存在错误

    • 使用带版本号的API操作节点
    • 先检查节点是否存在再操作
  3. 会话过期

    • 增加会话超时时间
    • 实现会话过期后的重连逻辑

八、总结

本文从Zookeeper的基本概念讲起,详细介绍了安装配置、基本操作、Java客户端使用,到SpringBoot整合,再到高级应用如分布式锁、配置中心、服务注册与发现等。通过完整的代码示例和详细注释,希望能帮助读者从零开始掌握Zookeeper的使用。

Zookeeper作为分布式系统的基石,其强大的协调能力可以帮助我们解决分布式环境中的各种难题。但在实际生产环境中,还需要根据具体场景合理设计和使用,并注意性能优化和监控运维。

完整的SpringBoot整合Zookeeper项目代码可以在GitHub上找到:项目地址

【Zookeeper从入门到实战】SpringBoot整合完整指南的更多相关文章

  1. Spring Boot从入门到实战:整合Web项目常用功能

    在Web应用开发过程中,一般都涵盖一些常用功能的实现,如数据库访问.异常处理.消息队列.缓存服务.OSS服务,以及接口日志配置,接口文档生成等.如果每个项目都来一套,则既费力又难以维护.可以通过Spr ...

  2. Spring Boot从入门到实战:整合通用Mapper简化单表操作

    数据库访问是web应用必不可少的部分.现今最常用的数据库ORM框架有Hibernate与Mybatis,Hibernate貌似在传统IT企业用的较多,而Mybatis则在互联网企业应用较多.通用Map ...

  3. 一份从入门到精通NLP的完整指南 | NLPer

    该小博主介绍 本人:笔名zenRRan,方向自然语言处理,方法主要是深度学习. 未来的目标:人工智能之自然语言处理博士. 写公众号目的:将知识变成开源,让每个渴求知识而难以入门人工智能的小白以及想进阶 ...

  4. SpringBoot整合Shiro+MD5+Salt+Redis实现认证和动态权限管理(上)----筑基中期

    写在前面 通过前几篇文章的学习,我们从大体上了解了shiro关于认证和授权方面的应用.在接下来的文章当中,我将通过一个demo,带领大家搭建一个SpringBoot整合Shiro的一个项目开发脚手架, ...

  5. Spring Boot 从入门到实战汇总

    之前写过几篇spring boot入门到实战的博文,因为某些原因没能继续. 框架更新迭代很快,之前还是基于1.x,现在2.x都出来很久了.还是希望能从基于该框架项目开发的整体有一个比较系统的梳理,于是 ...

  6. java springboot整合zookeeper入门教程(增删改查)

    java springboot整合zookeeper增删改查入门教程 zookeeper的安装与集群搭建参考:https://www.cnblogs.com/zwcry/p/10272506.html ...

  7. Springboot整合Redis入门完整篇,零基础入门教学教程

    记录一次简易集成Redis缓存 自定义Redisconfig配置 自定义序列化操作 加深印像 整合前提工具环境准备: 1.redis官网 https://redis.io/download 下载安装r ...

  8. 【Spring Cloud & Alibaba全栈开源项目实战】:SpringBoot整合ELK实现分布式登录日志收集和统计

    一. 前言 其实早前就想计划出这篇文章,但是最近主要精力在完善微服务.系统权限设计.微信小程序和管理前端的功能,不过好在有群里小伙伴的一起帮忙反馈问题,基础版的功能已经差不多,也在此谢过,希望今后大家 ...

  9. SpringBoot从入门到精通二(SpringBoot整合myBatis的两种方式)

    前言 通过上一章的学习,我们已经对SpringBoot有简单的入门,接下来我们深入学习一下SpringBoot,我们知道任何一个网站的数据大多数都是动态的,也就是说数据是从数据库提取出来的,而非静态数 ...

  10. SpringBoot整合Redis、mybatis实战,封装RedisUtils工具类,redis缓存mybatis数据 附源码

    创建SpringBoot项目 在线创建方式 网址:https://start.spring.io/ 然后创建Controller.Mapper.Service包 SpringBoot整合Redis 引 ...

随机推荐

  1. npm ERR! request to https://registry.npm.taobao.org/axios failed, reason: certificate has expired

    前言 一直使用 npm build没问题的,突然出现报错: npm WARN install Usage of the `--dev` option is deprecated. Use `--onl ...

  2. 小白的第一篇blog

    Markdown学习 1.标题 要写标题可用#加空格,再下字,然后再用回车键. 2.字体 1.粗体打法:在文字两侧加两个* 如:hello world! 2.斜体打法:在文字两侧加一个* 如: hel ...

  3. langchain0.3教程:从0到1打造一个智能聊天机器人

    在上一篇文章<大模型开发之langchain0.3(一):入门篇> 中已经介绍了langchain开发框架的搭建,最后使用langchain实现了HelloWorld的代码案例,本篇文章将 ...

  4. 【Linux】3.9 网络配置

    网络配置 1 Linux网络配置原理 虚拟机NAT网络配置原理 2 查看网络IP和网关 2.1 虚拟机网络编辑器 2.2 修改IP地址 2.3 查看网关 2.4 查看windows中的虚拟网卡的ip地 ...

  5. 【单片机】滑稽AT89C52表情实现

    [单片机]滑稽AT89C52表情实现 零.原因 在群里看到了这样一个表情: 这是用51做的,刚好开发板上有8个小灯,想实现一下. 一.代码 新建工程,写入如下代码: #include<reg52 ...

  6. 0x03 搜索与图论

    搜索与图论 广度优先搜索\(BFS\) 概念 广度优先搜索(Breadth-First Search)是一种图遍历算法,用于在图或树中按层次逐层访问节点.它从源节点(起始节点)开始,首先访问源节点的所 ...

  7. FastAPI依赖注入实践:工厂模式与实例复用的优化策略

    title: FastAPI依赖注入实践:工厂模式与实例复用的优化策略 date: 2025/04/06 01:22:25 updated: 2025/04/06 01:22:25 author: c ...

  8. KGDB调试Linux内核与模块

    前言 内核 5.10 版本 openEuler 使用 yum install 下载了源码,并且通过两个 VMware 虚拟机进行调试 ubuntu 直接使用 git 拉取了https://kernel ...

  9. SynchronousQueue的put方法底层源码

    一.SynchronousQueue的put方法底层源码 SynchronousQueue 的 put 方法用于将元素插入队列.由于 SynchronousQueue 没有实际的存储空间,put 方法 ...

  10. 使用XML的方式编写:@Aspect运用

    例子. 接口 public interface Calculator { // 加 public int add(int i, int j); // 减 public int sub(int i, i ...