Zookeeper学习一
初识 Zookeeper
Zookeeper 是 Apache Hadoop 项目下的一个子项目,是一个树形目录服务(B树)。
Zookeeper 翻译过来就是 动物园管理员,他是用来管 Hadoop(大象)、Hive(蜜蜂)、Pig(小 猪)的管理员。简称zk
Zookeeper 是一个分布式的、开源的分布式应用程序的协调服务。
Zookeeper 提供的主要功能包括: 配置管理 分布式锁 集群管理
Zookeeper 安装与配置
1.1 下载安装
1、环境准备
ZooKeeper服务器是用Java创建的,它运行在JVM之上。需要安装JDK 7或更高版本。
2、上传
将下载的ZooKeeper放到/opt/ZooKeeper目录下
#上传zookeeper alt+p
put f:/setup/apache-zookeeper-3.5.6-bin.tar.gz
#打开 opt目录
cd /opt
#创建zooKeeper目录
mkdir zooKeeper
#将zookeeper安装包移动到 /opt/zooKeeper
mv apache-zookeeper-3.5.6-bin.tar.gz /opt/zookeeper/
3、解压
将tar包解压到/opt/zookeeper目录下
tar -zxvf apache-ZooKeeper-3.5.6-bin.tar.gz
1.2 配置启动
1、配置zoo.cfg
进入到conf目录拷贝一个zoo_sample.cfg并完成配置
#进入到conf目录
cd /opt/zooKeeper/apache-zooKeeper-3.5.6-bin/conf/
#拷贝
cp zoo_sample.cfg zoo.cfg
修改zoo.cfg
#打开目录
cd /opt/zooKeeper/
#创建zooKeeper存储目录
mkdir zkdata
#修改zoo.cfg
vim /opt/zooKeeper/apache-zooKeeper-3.5.6-bin/conf/zoo.cfg

修改存储目录:dataDir=/opt/zookeeper/zkdata
2、启动ZooKeeper
cd /opt/zooKeeper/apache-zooKeeper-3.5.6-bin/bin/
#启动./zkServer.sh start

看到上图表示ZooKeeper成功启动
3、查看ZooKeeper状态
./zkServer.sh status
zookeeper启动成功。standalone代表zk没有搭建集群,现在是单节点

zookeeper没有启动

Zookeeper 命令操作
Zookeeper 数据模型
ZooKeeper 是一个树形目录服务,其数据模型和Unix的文件系统目录树很类似,拥有一个层次化结构。


这里面的每一个节点都被称为: ZNode,每个节点上都会保存自己的数据和节点信息。
节点可以拥有子节点,同时也允许少量(1MB)数据存储在该节点之下。
节点可以分为四大类:
- PERSISTENT 持久化节点
- EPHEMERAL 临时节点 :-e
- PERSISTENT_SEQUENTIAL 持久化顺序节点 :-s
- EPHEMERAL_SEQUENTIAL 临时顺序节点 :-es
Zookeeper服务端常用命令
- 启动 ZooKeeper 服务: ./zkServer.sh start
- 查看 ZooKeeper 服务状态: ./zkServer.sh status
- 停止 ZooKeeper 服务: ./zkServer.sh stop
- 重启 ZooKeeper 服务: ./zkServer.sh restart
Zookeeper客户端常见命令
连接ZooKeeper服务器:./zkCli.sh –server ip:port
断开连接:quit
设置节点值:set /节点path value
查看命令帮助:help
删除单个节点:delete /节点path
显示指定目录下节点:ls 目录
删除带有子节点的节点:deleteall /节点path
创建节点:create /节点path value
获取节点值:get /节点path
Zookeeper JavaAPI 操作
建立连接
建立连接有两种方式,一种是调用工厂对象的newClient()方法,另一种就是调用工厂对象的builder(),通过链式调用的方法就连接信息传入工厂中。
下面是代码示例:
/*** 建立连接*/@Beforepublic void testConnect() {/*** @param connectString 连接字符串。zk server 地址和端口 "192.168.149.135:2181,192.168.149.136:2181"* @param sessionTimeoutMs 会话超时时间 单位ms* @param connectionTimeoutMs 连接超时时间 单位ms* @param retryPolicy 重试策略*//* //重试策略RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,10);//1.第一种方式CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.149.135:2181",60 * 1000, 15 * 1000, retryPolicy);*///重试策略RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000, 10);//2.第二种方式//CuratorFrameworkFactory.builder();client = CuratorFrameworkFactory.builder().connectString("192.168.149.135:2181").sessionTimeoutMs(60 * 1000).connectionTimeoutMs(15 * 1000).retryPolicy(retryPolicy)
//命名空间,使创建的节点都在命名空间的路径下面.namespace("kjz").build();//开启连接client.start();}
注意此方法需要加上@Before注解,表示其他测试方法执行前需要先执行加了@Before注解的方法。因为每次进行crud操作时都需要与ZooKeeper Server建立连接。
建立了连接,操作完毕后同时需要释放连接,在对应方法上面加一个@After注解,表示每次进行测试最后都要执行该方法。
@Afterpublic void close() {if (client != null) {client.close();}}
添加节点
代码示例如下:
/*** 创建节点:create 持久 临时 顺序 数据* 1. 基本创建 :create().forPath("")* 2. 创建节点 带有数据:create().forPath("",data)* 3. 设置节点的类型:create().withMode().forPath("",data)* 4. 创建多级节点 /app1/p1 :create().creatingParentsIfNeeded().forPath("",data)*/@Testpublic void testCreate() throws Exception {//2. 创建节点 带有数据//如果创建节点,没有指定数据,则默认将当前客户端的ip作为数据存储String path = client.create().forPath("/app2", "hehe".getBytes());System.out.println(path);}@Testpublic void testCreate2() throws Exception {//1. 基本创建//如果创建节点,没有指定数据,则默认将当前客户端的ip作为数据存储String path = client.create().forPath("/app1");System.out.println(path);}@Testpublic void testCreate3() throws Exception {//3. 设置节点的类型//默认类型:持久化String path = client.create().withMode(CreateMode.EPHEMERAL).forPath("/app3");System.out.println(path);}@Testpublic void testCreate4() throws Exception {//4. 创建多级节点 /app1/p1//creatingParentsIfNeeded():如果父节点不存在,则创建父节点String path = client.create().creatingParentsIfNeeded().forPath("/app4/p1");System.out.println(path);}
Curator中提供了一个枚举类,里面定义了设置不同类型节点的常量

运行testCreate3()时我发现ZooKeeper中并没有保存我创建的节点,原因是在创建节点时指定的节点类型为临时节点,临时节点在会话结束后就会删除。我通过运行testCreate3()这个方法来创建一个节点,运行结束,说明会话也结束了,ZooKeeper就会把节点删除了。
删除节点
删除节点: delete deleteall
删除单个节点:delete().forPath("/app1");
删除带有子节点的节点:delete().deletingChildrenIfNeeded().forPath("/app1");
必须成功的删除:为了防止网络抖动。本质就是重试。client.delete().guaranteed().forPath("/app2");
回调:inBackground;
代码示例:
/*** 删除节点: delete deleteall* 1. 删除单个节点:delete().forPath("/app1");* 2. 删除带有子节点的节点:delete().deletingChildrenIfNeeded().forPath("/app1");* 3. 必须成功的删除:为了防止网络抖动。本质就是重试。 client.delete().guaranteed().forPath("/app2");* 4. 回调:inBackground* @throws Exception*/@Testpublic void testDelete() throws Exception {// 1. 删除单个节点client.delete().forPath("/app1");}@Testpublic void testDelete2() throws Exception {//2. 删除带有子节点的节点client.delete().deletingChildrenIfNeeded().forPath("/app4");}@Testpublic void testDelete3() throws Exception {//3. 必须成功的删除client.delete().guaranteed().forPath("/app2");}@Testpublic void testDelete4() throws Exception {//4. 回调client.delete().guaranteed().inBackground(new BackgroundCallback(){@Overridepublic void processResult(CuratorFramework client, CuratorEvent event) throws Exception {System.out.println("我被删除了~");System.out.println(event);}}).forPath("/app1");}
修改节点
基本修改数据 setData().forPath()
根据版本修改数据 setData().withVersion().forPath()
version 是通过查询出来的。目的就是为了让其他客户端或者线程不干扰我。
代码示例:
/*** 修改数据* 1. 基本修改数据:setData().forPath()* 2. 根据版本修改: setData().withVersion().forPath()* * version 是通过查询出来的。目的就是为了让其他客户端或者线程不干扰我。** @throws Exception*/@Testpublic void testSet() throws Exception {client.setData().forPath("/app1", "itcast".getBytes());}@Testpublic void testSetForVersion() throws Exception {Stat status = new Stat();//3. 查询节点状态信息:ls -sclient.getData().storingStatIn(status).forPath("/app1");int version = status.getVersion();//查询出来的 3System.out.println(version);client.setData().withVersion(version).forPath("/app1", "hehe".getBytes());}
查询节点
查询数据:get: getData().forPath()
查询子节点: ls: getChildren().forPath()
查询节点状态信息:ls -s:getData().storingStatIn(状态对象).forPath()
代码示例:
@Testpublic void testGet1() throws Exception {//1. 查询数据:getbyte[] data = client.getData().forPath("/app1");System.out.println(new String(data));}@Testpublic void testGet2() throws Exception {// 2. 查询子节点: lsList<String> path = client.getChildren().forPath("/");System.out.println(path);}@Testpublic void testGet3() throws Exception {Stat status = new Stat();System.out.println(status);//3. 查询节点状态信息:ls -sclient.getData().storingStatIn(status).forPath("/app1");System.out.println(status);}
Stat里面封装了节点的状态信息

完整代码如下:
package com.kjz.curator;import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.BackgroundCallback;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;import java.util.List;public class CuratorTest {private CuratorFramework client;/*** 建立连接*/@Beforepublic void testConnect() {/*** @param connectString 连接字符串。zk server 地址和端口 "192.168.149.135:2181,192.168.149.136:2181"* @param sessionTimeoutMs 会话超时时间 单位ms* @param connectionTimeoutMs 连接超时时间 单位ms* @param retryPolicy 重试策略*//* //重试策略RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,10);//1.第一种方式CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.149.135:2181",60 * 1000, 15 * 1000, retryPolicy);*///重试策略RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000, 10);//2.第二种方式//CuratorFrameworkFactory.builder();client = CuratorFrameworkFactory.builder().connectString("192.168.149.135:2181").sessionTimeoutMs(60 * 1000).connectionTimeoutMs(15 * 1000).retryPolicy(retryPolicy).namespace("kjz").build();//开启连接client.start();}
//==============================create=============================================================================/*** 创建节点:create 持久 临时 顺序 数据* 1. 基本创建 :create().forPath("")* 2. 创建节点 带有数据:create().forPath("",data)* 3. 设置节点的类型:create().withMode().forPath("",data)* 4. 创建多级节点 /app1/p1 :create().creatingParentsIfNeeded().forPath("",data)*/@Testpublic void testCreate() throws Exception {//2. 创建节点 带有数据//如果创建节点,没有指定数据,则默认将当前客户端的ip作为数据存储String path = client.create().forPath("/app2", "hehe".getBytes());System.out.println(path);}@Testpublic void testCreate2() throws Exception {//1. 基本创建//如果创建节点,没有指定数据,则默认将当前客户端的ip作为数据存储String path = client.create().forPath("/app1");System.out.println(path);}@Testpublic void testCreate3() throws Exception {//3. 设置节点的类型//默认类型:持久化String path = client.create().withMode(CreateMode.EPHEMERAL).forPath("/app3");System.out.println(path);}@Testpublic void testCreate4() throws Exception {//4. 创建多级节点 /app1/p1//creatingParentsIfNeeded():如果父节点不存在,则创建父节点String path = client.create().creatingParentsIfNeeded().forPath("/app4/p1");System.out.println(path);}
//===========================get================================================================================/*** 查询节点:* 1. 查询数据:get: getData().forPath()* 2. 查询子节点: ls: getChildren().forPath()* 3. 查询节点状态信息:ls -s:getData().storingStatIn(状态对象).forPath()*/@Testpublic void testGet1() throws Exception {//1. 查询数据:getbyte[] data = client.getData().forPath("/app1");System.out.println(new String(data));}@Testpublic void testGet2() throws Exception {// 2. 查询子节点: lsList<String> path = client.getChildren().forPath("/");System.out.println(path);}@Testpublic void testGet3() throws Exception {Stat status = new Stat();System.out.println(status);//3. 查询节点状态信息:ls -sclient.getData().storingStatIn(status).forPath("/app1");System.out.println(status);}//===========================set================================================================================/*** 修改数据* 1. 基本修改数据:setData().forPath()* 2. 根据版本修改: setData().withVersion().forPath()* * version 是通过查询出来的。目的就是为了让其他客户端或者线程不干扰我。** @throws Exception*/@Testpublic void testSet() throws Exception {client.setData().forPath("/app1", "kjz".getBytes());}@Testpublic void testSetForVersion() throws Exception {Stat status = new Stat();//3. 查询节点状态信息:ls -sclient.getData().storingStatIn(status).forPath("/app1");int version = status.getVersion();//查询出来的 3System.out.println(version);client.setData().withVersion(version).forPath("/app1", "hehe".getBytes());}//===========================delete================================================================================/*** 删除节点: delete deleteall* 1. 删除单个节点:delete().forPath("/app1");* 2. 删除带有子节点的节点:delete().deletingChildrenIfNeeded().forPath("/app1");* 3. 必须成功的删除:为了防止网络抖动。本质就是重试。 client.delete().guaranteed().forPath("/app2");* 4. 回调:inBackground* @throws Exception*/@Testpublic void testDelete() throws Exception {// 1. 删除单个节点client.delete().forPath("/app1");}@Testpublic void testDelete2() throws Exception {//2. 删除带有子节点的节点client.delete().deletingChildrenIfNeeded().forPath("/app4");}@Testpublic void testDelete3() throws Exception {//3. 必须成功的删除client.delete().guaranteed().forPath("/app2");}@Testpublic void testDelete4() throws Exception {//4. 回调client.delete().guaranteed().inBackground(new BackgroundCallback(){@Overridepublic void processResult(CuratorFramework client, CuratorEvent event) throws Exception {System.out.println("我被删除了~");System.out.println(event);}}).forPath("/app1");}@Afterpublic void close() {if (client != null) {client.close();}}}
Watch事件监听
- ZooKeeper 允许用户在指定节点上注册一些Watcher,并且在一些特定事件触发的时候,ZooKeeper 服务端会将事件通知到感兴趣的客户端上去,该机制是 ZooKeeper 实现分布式协调服务的重要特性。
- ZooKeeper 中引入了Watcher机制来实现了发布/订阅功能能,能够让多个订阅者同时监听某一个对象,当一个对象自身状态变化时,会通知所有订阅者。
- ZooKeeper 原生支持通过注册Watcher来进行事件监听,但是其使用并不是特别方便 需要开发人员自己反复注册Watcher,比较繁琐。
- Curator引入了 Cache 来实现对 ZooKeeper 服务端事件的监听。
ZooKeeper提供了三种Watcher:
- NodeCache : 只是监听某一个特定的节点
- PathChildrenCache : 监控一个ZNode的子节点.
- TreeCache : 可以监控整个树上的所有节点,类似于PathChildrenCache和NodeCache 的组合
演示 NodeCache:给指定一个节点注册监听器
/*** 演示 NodeCache:给指定一个节点注册监听器*/@Testpublic void testNodeCache() throws Exception {//1. 创建NodeCache对象final NodeCache nodeCache = new NodeCache(client,"/app1");//2. 注册监听nodeCache.getListenable().addListener(new NodeCacheListener() {@Overridepublic void nodeChanged() throws Exception {System.out.println("节点变化了~");//获取修改节点后的数据byte[] data = nodeCache.getCurrentData().getData();System.out.println(new String(data));}});//3. 开启监听.如果设置为true,则开启监听是,加载缓冲数据nodeCache.start(true);while (true){}}
演示 PathChildrenCache:监听某个节点的所有子节点们
/*** 演示 PathChildrenCache:监听某个节点的所有子节点们*/@Testpublic void testPathChildrenCache() throws Exception {//1.创建监听对象PathChildrenCache pathChildrenCache = new PathChildrenCache(client,"/app2",true);//2. 绑定监听器pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {@Overridepublic void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {System.out.println("子节点变化了~");System.out.println(event);//监听子节点的数据变更,并且拿到变更后的数据//1.获取类型PathChildrenCacheEvent.Type type = event.getType();//2.判断类型是否是updateif(type.equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)){System.out.println("数据变了!!!");byte[] data = event.getData().getData();System.out.println(new String(data));}}});//3. 开启pathChildrenCache.start();while (true){}}

数据变更类型的枚举类。
演示 TreeCache:监听某个节点自己和所有子节点们,相当于PathChildrenCache和NodeCache的组合
/*** 演示 NodeCache:给指定一个节点注册监听器*/@Testpublic void testNodeCache() throws Exception {//1. 创建NodeCache对象final NodeCache nodeCache = new NodeCache(client,"/app1");//2. 注册监听nodeCache.getListenable().addListener(new NodeCacheListener() {@Overridepublic void nodeChanged() throws Exception {System.out.println("节点变化了~");//获取修改节点后的数据byte[] data = nodeCache.getCurrentData().getData();System.out.println(new String(data));}});//3. 开启监听.如果设置为true,则开启监听是,加载缓冲数据nodeCache.start(true);while (true){}}
完整代码如下:
package com.kjz.curator;import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.BackgroundCallback;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.framework.recipes.cache.*;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;import java.util.List;public class CuratorWatcherTest {private CuratorFramework client;/*** 建立连接*/@Beforepublic void testConnect() {/*** @param connectString 连接字符串。zk server 地址和端口 "192.168.149.135:2181,192.168.149.136:2181"* @param sessionTimeoutMs 会话超时时间 单位ms* @param connectionTimeoutMs 连接超时时间 单位ms* @param retryPolicy 重试策略*//* //重试策略RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,10);//1.第一种方式CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.149.135:2181",60 * 1000, 15 * 1000, retryPolicy);*///重试策略RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000, 10);//2.第二种方式//CuratorFrameworkFactory.builder();client = CuratorFrameworkFactory.builder().connectString("192.168.149.135:2181").sessionTimeoutMs(60 * 1000).connectionTimeoutMs(15 * 1000).retryPolicy(retryPolicy).namespace("kjz").build();//开启连接client.start();}@Afterpublic void close() {if (client != null) {client.close();}}/*** 演示 NodeCache:给指定一个节点注册监听器*/@Testpublic void testNodeCache() throws Exception {//1. 创建NodeCache对象final NodeCache nodeCache = new NodeCache(client,"/app1");//2. 注册监听nodeCache.getListenable().addListener(new NodeCacheListener() {@Overridepublic void nodeChanged() throws Exception {System.out.println("节点变化了~");//获取修改节点后的数据byte[] data = nodeCache.getCurrentData().getData();System.out.println(new String(data));}});//3. 开启监听.如果设置为true,则开启监听是,加载缓冲数据nodeCache.start(true);while (true){}}/*** 演示 PathChildrenCache:监听某个节点的所有子节点们*/@Testpublic void testPathChildrenCache() throws Exception {//1.创建监听对象PathChildrenCache pathChildrenCache = new PathChildrenCache(client,"/app2",true);//2. 绑定监听器pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {@Overridepublic void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {System.out.println("子节点变化了~");System.out.println(event);//监听子节点的数据变更,并且拿到变更后的数据//1.获取类型PathChildrenCacheEvent.Type type = event.getType();//2.判断类型是否是updateif(type.equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)){System.out.println("数据变了!!!");byte[] data = event.getData().getData();System.out.println(new String(data));}}});//3. 开启pathChildrenCache.start();while (true){}}/*** 演示 TreeCache:监听某个节点自己和所有子节点们*/@Testpublic void testTreeCache() throws Exception {//1. 创建监听器TreeCache treeCache = new TreeCache(client,"/app2");//2. 注册监听treeCache.getListenable().addListener(new TreeCacheListener() {@Overridepublic void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {System.out.println("节点变化了");System.out.println(event);}});//3. 开启treeCache.start();while (true){}}}
分布式锁实现
Zookeeper分布式锁原理
核心思想:当客户端要获取锁,则创建节点,使用完锁,则删除该节点。
- 客户端获取锁时,在lock节点下创建临时顺序节点。临时:防止获取到锁的服务宕机了导致锁无法释放,临时节点会在会话结束后自动删除。顺序:找到最小节点,创建最小节点的服务获取到锁。
- 然后获取lock下面的所有子节点,客户端获取到所有的子节点之后,如果发现自己创建的子节点序号最小,那么就认为该客户端获取到了锁。使用完锁后,将该节点删除。
- 如果发现自己创建的节点并非lock所有子节点中最小的,说明自己还没有获取到锁,此时客户端需要找到比自己小的那个节点,同时对其注册事件监听器,监听删除事件。
- 如果发现比自己小的那个节点被删除,则客户端的 Watcher会收到相应通知,此时再次判断自己创建的节点 是否是lock子节点中序号最小的,如果是则获取到了锁,如果不是则重复以上步骤继续获取到比自己小的一个节点 并注册监听。
Curator实现分布式锁API
在Curator中有五种锁方案:
- InterProcessSemaphoreMutex:分布式排它锁(非可重入锁)
- InterProcessMutex:分布式可重入排它锁
- InterProcessReadWriteLock:分布式读写锁
- InterProcessMultiLock:将多个锁作为单个实体管理的容器
- InterProcessSemaphoreV2:共享信号量
案例:模拟12306售票
模拟12306服务
package com.kjz.curator;import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;import java.util.concurrent.TimeUnit;public class Ticket12306 implements Runnable{private int tickets = 10;//数据库的票数private InterProcessMutex lock ;public Ticket12306(){//重试策略RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000, 10);//2.第二种方式//CuratorFrameworkFactory.builder();CuratorFramework client = CuratorFrameworkFactory.builder().connectString("192.168.149.135:2181").sessionTimeoutMs(60 * 1000).connectionTimeoutMs(15 * 1000).retryPolicy(retryPolicy).build();//开启连接client.start();lock = new InterProcessMutex(client,"/lock");}@Overridepublic void run() {while(true){//获取锁try {lock.acquire(3, TimeUnit.SECONDS);if(tickets > 0){System.out.println(Thread.currentThread()+":"+tickets);Thread.sleep(100);tickets--;}} catch (Exception e) {e.printStackTrace();}finally {//释放锁try {lock.release();} catch (Exception e) {e.printStackTrace();}}}}
}
模拟买票
package com.kjz.curator;import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.*;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;public class LockTest {public static void main(String[] args) {Ticket12306 ticket12306 = new Ticket12306();//创建客户端Thread t1 = new Thread(ticket12306,"携程");Thread t2 = new Thread(ticket12306,"飞猪");t1.start();t2.start();}}
相关文章:
Zookeeper学习一
初识 Zookeeper Zookeeper 是 Apache Hadoop 项目下的一个子项目,是一个树形目录服务(B树)。 Zookeeper 翻译过来就是 动物园管理员,他是用来管 Hadoop(大象)、Hive(蜜蜂)、Pig(小 猪)的管理员。简称zk …...
SAR教程系列7——在cadence中用Spectrum工具FFT仿真ADC的ENOB、SNR等动态性能指标
首先在仿真之前,你得有一个ADC。然后是思考如何仿真的问题,如何加激励,如何使用相关工具查看仿真结果。假定你有一个可以仿真的ADC,大致经过下列步骤可以得到ADC的相关动态性能指标。 第一步:在ADC后面接一个理想的DA…...
攻防世界:mfw[WriteUP]
根据题目提示考虑是git库泄露 这里在地址栏后加.git也可以验证是git库泄露 使用GitHack工具对git库进行恢复重建 在templates目录下存在flag.php文件,但里面并没有flag 有内容的只有主目录下的index.php index.php源码: <?phpif (isset($_GET[page…...
mysq性能优化-my.cnf配置文件参数调整
MySQL 优化配置文件(my.cnf 或 my.ini)是调整 MySQL 服务器性能的重要手段之一。以下是一些常见的场景,可以通过调整配置文件参数值来优化 MySQL: 1. **提高并发处理能力**: - innodb_buffer_pool_size:增…...
ddres( ) 组站星双差方程和设计矩阵
1 ddres( )参数介绍 rtklib中进行的单频解算 双差观测值,单差的模糊度 单频点双差 DD (double-differenced) phase/code residuals ------------------------------ x 模糊度 P 方差-协方差阵 sat 共识卫星列表 ns 共识卫星数量 y…...
【OpenCV】图像像素的遍历
1 前言 介绍两种遍历像素的方法(非指针、指针)。注意:.at() .ptr()的作用、用法。相关API: Mat对象.ptr() Mat对象.at() 2 代码及内容 #include "iostream" #include "opencv2/opencv.hpp"using namespac…...
(超简单)构建高可用网络应用:使用Nginx进行负载均衡与健康检查
当构建高可用的网络应用时,负载均衡是至关重要的技术之一。Nginx 是一个强大的开源反向代理服务器,提供了丰富的负载均衡功能,包括负载均衡算法和健康检查。在本篇博客中,我们将讨论如何使用 Nginx 进行负载均衡,并结合…...
华为OD面试手撕算法-合并排序数组
题目描述 本题是leetcode一道简单题:合并两个有序数组,但是对于时间和空间复杂度面试官明确给出了限制。 // 给定两个排序后的数组 A 和 B,其中 A 的末端有足够的缓冲空间容纳 B。 编写一个方法,将 B 合并入 A 并排序。 // 初始化…...
云智慧发布对象关系型数据库CloudPanguDB,打破传统技术壁垒
近日,云智慧推出关系型数据库CloudPanguDB(中文名称:盘古数据库),旨在通过高兼容性能和创新技术架构,降低企业项目整体运营成本。 无论是处理海量复杂数据,还是构建清晰有序的数据结构关系&…...
6.8物联网RK3399项目开发实录-驱动开发之RTC实时时钟的使用(wulianjishu666)
90款行业常用传感器单片机程序及资料【stm32,stc89c52,arduino适用】 链接:https://pan.baidu.com/s/1M3u8lcznKuXfN8NRoLYtTA?pwdc53f RTC 使用 简介 AIO-3399J 开发板上有 一个集成于 RK808 上的RTC(Real Time Clock),主要功能有时钟,…...
VUE——概述
vue是前端框架,基于MVVM思想。 引入 从官网下载vue文件 <script src"js/vue.js"></script> 定义vue对象 new Vue({el: "#x",//vue接管区域,#表示选择器,x是id名字data: {message: "y"} })案例…...
合宙4G模块Air724UG调试过程(短信发送、上传数据到华为云IOT)
合宙Air724UG-4G模块AT指令调试接线演示 一、前言 上海合宙Air724UG模块是一款高性能的4G Cat.1通信模组(全网通模块,支持移动、联通、电信,支持短信和网络通信),为开发者提供了丰富的接口和开发方式。 在本文中,将详述调试与集成该模块的关键步骤: (1)从基础硬件配…...
【项目新功能开发篇】需求分析和开发设计
作者介绍:本人笔名姑苏老陈,从事JAVA开发工作十多年了,带过大学刚毕业的实习生,也带过技术团队。最近有个朋友的表弟,马上要大学毕业了,想从事JAVA开发工作,但不知道从何处入手。于是࿰…...
CentOS 7 下离线安装RabbitMQ教程
CentOS 7 下安装RabbitMQ教程一、做准备(VMWare 虚拟机上的 CentOS 7 镜像 上安装的) (1)准备RabbitMQ的安装包(rabbitmq-server-3.8.5-1.el7.noarch)下载地址mq https://github.com/rabbitmq/rabbitmq-se…...
【Servlet】session保存作用域
session保存作用域:一次会话范围都有效 Java的服务器端,有一块内存专门存储在session保存作用域的数据。 session保存作用域是和具体的某一个session对应的。 常用API: void session.setAttribute(k, v)Object session.getAttrivute(k) —…...
企业周年庆3d云展厅促进了客企间交流与互动
在数字化浪潮席卷而来的今天,传统的展示方式已难以满足现代人对信息获取与体验的高标准需求。为此,一种革命性的展示方式——线上3D虚拟展厅应运而生,以其独特的魅力逐渐引领展示方式的革新。 线上3D虚拟展厅开发,不仅为参与者带来…...
Android Studio学习5——布局layout与视图view
wrap_content,内容有多大,就有多宽(包裹) 布局 padding 边框与它自身的内容 margin 控件与控件之间...
设计模式(15):迭代器模式
介绍 提供一中可以遍历聚合对象的方式。又称为: 游标cursor模式 迭代器模式角色 抽象聚合类(Aggregate):提供了聚合相关的方法,并提供获取迭代器的方法;具体集合类(ConcreteAggregate):实现了抽象聚合类;抽象迭代器(Iterator):…...
前端内部技术分享---前端组件之表格组件的封装与使用(Vue3)
业务背景 在我们接触的项目中,PC端的项目中基本上百分之60或以上,都会用到表格,我们最常用的 就是element-plus 组件库,相信大家都对el-table 都比较熟悉了,但是在许许多多大同小异的界面中,每次都要写很多…...
【一】Mac 本地部署大模型
盘古开天辟地开始 # 安装brew/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"(echo; echo eval "$(/opt/homebrew/bin/brew shellenv)") >> /Users/wangxin52/.zprofileeval "$(/opt/homeb…...
SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...
七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist
现象: android studio报错: [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决: 不要动CMakeLists.…...
提升移动端网页调试效率:WebDebugX 与常见工具组合实践
在日常移动端开发中,网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时,开发者迫切需要一套高效、可靠且跨平台的调试方案。过去,我们或多或少使用过 Chrome DevTools、Remote Debug…...
