Zookeeper基础入门-2【ZooKeeper 分布式锁案例】

Zookeeper基础入门-2【ZooKeeper 分布式锁案例】

  • 四、ZooKeeper-IDEA环境搭建
    • 4.1.环境搭建
      • 4.1.1.创建maven工程:zookeeper
      • 4.1.2.在pom文件添加依赖
      • 4.1.3.在项目的src/main/resources 目录下,新建文件为“log4j.properties”
      • 4.1.4.创建包名com.orange.zk
    • 4.2.ZooKeeper 客户端API操作
      • 4.2.1.初始化ZooKeeper对象
      • 4.2.2.创建节点
      • 4.2.3.获取子节点并监听节点变化
      • 4.2.4.判断节点Node是否存在
    • 4.3.客户端向服务端写数据流程
      • 4.3.1.写流程之写入请求直接发送给Leader节点
      • 4.3.2.写流程之写入请求发送给follower节点
  • 五、服务器动态上下线监听案例
    • 5.1.需求
    • 5.2.需求分析--服务器动态上下线
    • 5.3.具体实现
      • 5.3.1.先在集群上创建/servers 节点
      • 5.3.2.创建包名:com.orange.zkcase1
      • 5.3.3.服务器端向Zookeeper注册代码
      • 5.3.4.客户端代码
    • 5.4.测试
      • 5.4.1.在Linux 命令行上操作增加减少服务器
        • 5.4.1.1.启动DistributeClient 客户端
        • 5.4.1.2.在host128 上zk 的客户端/servers 目录上创建临时带序号节点
        • 5.4.1.3.观察Idea 控制台变化
        • 5.4.1.4.执行删除操作
        • 5.4.1.5.观察Idea 控制台变化
      • 5.4.2.在Idea 上操作增加减少服务器
        • 5.4.2.1.启动DistributeClient 客户端(如果已经启动过,不需要重启)
        • 5.4.2.2.启动DistributeServer 服务
          • 5.4.2.2.1.点击Edit Configurations…
          • 5.4.2.2.2.在弹出的窗口中(Program arguments)输入想启动的主机,例如,host130
          • 5.4.2.2.3.回到DistributeServer的main方法,右 键,在弹出的窗口中点击 Run “DistributeServer.main()”
          • 5.4.2.2.4.观察DistributeServer 控制台
          • 5.4.2.2.5.观察DistributeClient 控制台
  • 六、ZooKeeper 分布式锁案例
    • 6.1.分布式锁
    • 6.2.ZooKeeper分布式锁原理
    • 6.3.分布式锁案例分析
    • 6.3.原生Zookeeper 实现分布式锁案例
      • 6.3.1.分布式锁实现
      • 6.3.2.分布式锁测试
        • 6.3.2.1.创建两个线程
        • 6.3.2.2.观察控制台变化
    • 6.4.Curator框架实现分布式锁案例
      • 6.4.1.Curator有五种锁方案:
      • 6.4.1.原生的Java API 开发存在的问题
      • 6.4.2.Curator是一个专门解决分布式锁的框架,解决了原生Java API开发分布式遇到的问题
      • 6.4.3.Curator 案例实操
        • 6.4.3.1.添加依赖
        • 6.4.3.2.代码实现
        • 6.4.3.3.控制台变化
  • 七、模拟12306售票案例
    • 7.1.代码实现
    • 7.2.测试
    • 7.3.控制台变化
  • 八、企业面试真题(面试重点)
    • 8.1.选举机制
    • 8.2.生产集群安装多少zk 合适
    • 8.3.常用命令
  • endl

四、ZooKeeper-IDEA环境搭建

保证三台Zookeeper 集群服务端启动

[root@host128 ~]# jpsall
显示集群的所有java进程状态
=============== host128 ===============
66496 Jps
2445 QuorumPeerMain
=============== host129 ===============
66162 Jps
2413 QuorumPeerMain
=============== host130 ===============
65947 Jps
2383 QuorumPeerMain
执行结束

4.1.环境搭建

4.1.1.创建maven工程:zookeeper

4.1.2.在pom文件添加依赖

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>zookeeper-test01</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging><properties><maven.complier.source>8</maven.complier.source><maven.complier.target>8</maven.complier.target></properties><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>RELEASE</version></dependency><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.8.2</version></dependency><dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>3.5.7</version></dependency></dependencies>
</project>

4.1.3.在项目的src/main/resources 目录下,新建文件为“log4j.properties”

log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/spring.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n

4.1.4.创建包名com.orange.zk

在这里插入图片描述

4.2.ZooKeeper 客户端API操作

4.2.1.初始化ZooKeeper对象

/*** Description: zookeeper客户端*/
public class Client {//注意:connectString逗号左右不能有空格,否则连接不上private String connectString = "192.168.147.128:2181,192.168.147.129:2181,192.168.147.130:2181";//tickTime为2000,initLimit为10//LF初次连接时的超时时间应起码大于延迟时间tickTime*initLimit的值否则会因为超时而连接失败。private int sessionTimeout = 200000;private ZooKeeper zkClient;/*** 初始化ZooKeeper对象,必须要先创建,再进行创建节点,否则报空指针异常** @throws IOException*/@Beforepublic void init() throws IOException {zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {@Overridepublic void process(WatchedEvent watchedEvent) {}});}}

4.2.2.创建节点

    @Testpublic void create() throws InterruptedException, KeeperException {//"/tang":创建的节点的路径;//"t.avi".getBytes():节点里面的值,需要转化为字节传输;//ZooDefs.Ids.OPEN_ACL_UNSAFE:权限控制,设置访问权限;//CreateMode.PERSISTENT:创建的节点类型,如这个是永久不带序号节点。String nodeCreated = zkClient.create("/tang", "t.avi".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);}
# 指定启动host128的客户端,而不是localhost的
/opt/module/zookeeper-3.5.7/bin/zkCli.sh -server host128:2181
[zk: host128:2181(CONNECTED) 0] ls /
[tang, zookeeper]
[zk: host128:2181(CONNECTED) 1] get -s /tang
t.avi
cZxid = 0x100000002
ctime = Wed Feb 28 12:35:13 CST 2024
mZxid = 0x100000002
mtime = Wed Feb 28 12:35:13 CST 2024
pZxid = 0x100000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0

4.2.3.获取子节点并监听节点变化

/*** Description: zookeeper客户端*/
public class Client {//注意:connectString逗号左右不能有空格,否则连接不上private String connectString = "192.168.147.128:2181,192.168.147.129:2181,192.168.147.130:2181";//tickTime为2000,initLimit为10//LF初次连接时的超时时间应起码大于延迟时间tickTime*initLimit的值否则会因为超时而连接失败。private int sessionTimeout = 200000;private ZooKeeper zkClient;/*** 初始化ZooKeeper对象,必须要先创建,再进行创建节点,否则报空指针异常** @throws IOException*/@Beforepublic void init() throws IOException {zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {@Overridepublic void process(WatchedEvent watchedEvent) {//收到时间通知后的回调函数(用户的业务逻辑)System.out.println(watchedEvent.getType() + "--" + watchedEvent.getPath());//再次启动监听try {System.out.println("===============================");List<String> children = zkClient.getChildren("/", true);for (String child : children) {System.out.println(child);}System.out.println("===============================");} catch (Exception e) {e.printStackTrace();}}});}@Testpublic void create() throws InterruptedException, KeeperException {//"/tang":创建的节点的路径;//"t.avi".getBytes():节点里面的值,需要转化为字节传输;//ZooDefs.Ids.OPEN_ACL_UNSAFE:权限控制,设置访问权限;//CreateMode.PERSISTENT:创建的节点类型,如这个是永久不带序号节点。String nodeCreated = zkClient.create("/tang", "t.avi".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);}/*** 监听节点变化信息*/@Testpublic void getChildren() throws KeeperException, InterruptedException {System.out.println("-----------------------------");List<String> children = zkClient.getChildren("/", true);for (String child : children){System.out.println(child);}System.out.println("-----------------------------");//延时阻塞Thread.sleep(Long.MAX_VALUE);}}
None--null
===============================
tang
zookeeper
-----------------------------
tang
zookeeper
===============================

监听器只能监听一次,如果再发生变化需要重新注册监听器,要想每次节点发生变化都能检测到并且在控制台打印,就在初始化监听器里面再注册一个监听器,每次监听完又马上注册一个新的监听器。

# 指定启动host128的客户端,而不是localhost的
/opt/module/zookeeper-3.5.7/bin/zkCli.sh -server host128:2181
[zk: host128:2181(CONNECTED) 8] create /test01 "test01"
Created /test01
[zk: host128:2181(CONNECTED) 9] create /test02 "test02"
Created /test02
NodeChildrenChanged--/
===============================
tang
zookeeper
test01
===============================
NodeChildrenChanged--/
===============================
tang
test02
zookeeper
test01
===============================

4.2.4.判断节点Node是否存在

    /*** 判断节点是否存在*/@Testpublic void exist() throws InterruptedException, KeeperException {Stat stat = zkClient.exists("/tang", false);System.out.println(stat == null ? "not data" : "exist");}

4.3.客户端向服务端写数据流程

4.3.1.写流程之写入请求直接发送给Leader节点

在这里插入图片描述
1.当client向zookeeper的leader上写数据,发送一个写请求

2.这个Leader会把写请求广播给各个server,当各个server写成功后就会通知Leader.

3.当Leader收到半数以上server写成功应答,此时认为写成功,Client会收到Leader写成功应答。

4.3.2.写流程之写入请求发送给follower节点

在这里插入图片描述
1.当client向zookeeper集群的某个server上写数据,发送一个写请求

2.如果接收到请求的不是Leader,那么server会把请求转发给Leader,因为zookeeper的集群中只有一个是Leader,这个Leader会把写请求广播给各个server,当各个server写成功后就会通知Leader.

3.当Leader收到半数以上server写成功应答,此时认为写成功,Leader会告知向他提交申请的server

4.Server会进一步将通知Client写成功, 此时就认为写成功了。

五、服务器动态上下线监听案例

5.1.需求

某分布式系统中,主节点可以有多台,可以动态上下线,任意一台客户端都能实时感知到主节点服务器的上下线。

5.2.需求分析–服务器动态上下线

在这里插入图片描述

5.3.具体实现

5.3.1.先在集群上创建/servers 节点

[zk: host128:2181(CONNECTED) 10] create /servers "servers"
Created /servers

5.3.2.创建包名:com.orange.zkcase1

在这里插入图片描述

5.3.3.服务器端向Zookeeper注册代码

/*** Description: 服务端和zookeeper集群创建连接*/
public class DistributeServer {//注意:connectString逗号左右不能有空格,否则连接不上private String connectString = "192.168.147.128:2181,192.168.147.129:2181,192.168.147.130:2181";//tickTime为2000,initLimit为10//LF初次连接时的超时时间应起码大于延迟时间tickTime*initLimit的值否则会因为超时而连接失败。private int sessionTimeout = 200000;private ZooKeeper zkClient;public static void main(String[] args) throws IOException, InterruptedException, KeeperException {DistributeServer server = new DistributeServer();//1.连接zookeeper集群,获取zk连接,创建zkserver.getConnect();//2.注册服务器到zk集群server.regist(args[0]);//3.启动业务逻辑server.business();}private void business() throws InterruptedException {//延时阻塞Thread.sleep(Long.MAX_VALUE);}/*** 注册服务器,创建节点*/private void regist(String hostname) throws InterruptedException, KeeperException {String create = zkClient.create("/servers/" + hostname, hostname.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);System.out.println(hostname + " is online");}/*** 连接上zookeeper集群*/private void getConnect() throws IOException {zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {@Overridepublic void process(WatchedEvent watchedEvent) {}});}
}

5.3.4.客户端代码

/*** Description: 客户端监听集群节点的动态变化*/
public class DistributeClient {//注意:connectString逗号左右不能有空格,否则连接不上private String connectString = "192.168.147.128:2181,192.168.147.129:2181,192.168.147.130:2181";//tickTime为2000,initLimit为10//LF初次连接时的超时时间应起码大于延迟时间tickTime*initLimit的值否则会因为超时而连接失败。private int sessionTimeout = 200000;private ZooKeeper zkClient;public static void main(String[] args) throws IOException, InterruptedException, KeeperException {DistributeClient client = new DistributeClient();//1.获取zk连接client.getConnect();//2.监听服务器 /servers 下面子节点的增加和删除client.getServerList(); //获取servers上的所有节点的上线和下线//3.业务逻辑client.business();}private void business() throws InterruptedException {//延时阻塞Thread.sleep(Long.MAX_VALUE);}private void getServerList() throws InterruptedException, KeeperException {//获取servers下的所有节点信息List<String> children = zkClient.getChildren("/servers", true);//对父节点监听ArrayList<String> servers = new ArrayList<String>(); //集合用来存所有的服务器节点//遍历所有节点  获取节点中的主机名称信息for (String child : children) {byte[] data = zkClient.getData("/servers/" + child, false, null);servers.add(new String(data));}//打印服务器列表信息System.out.println(servers);}// 创建zookeeper客户端private void getConnect() throws IOException {zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {@Overridepublic void process(WatchedEvent watchedEvent) {//收到事件通知后的回调函数(用户的业务逻辑)try {//再次启动监听,避免只监听一次getServerList();} catch (KeeperException e) {e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();}}});}
}

5.4.测试

5.4.1.在Linux 命令行上操作增加减少服务器

5.4.1.1.启动DistributeClient 客户端
5.4.1.2.在host128 上zk 的客户端/servers 目录上创建临时带序号节点
[zk: host128:2181(CONNECTED) 0] ls /
[zookeeper]
[zk: host128:2181(CONNECTED) 1] create /servers "servers"
Created /servers
[zk: host128:2181(CONNECTED) 2] create -e -s /servers/host128 "hsot128"
Created /servers/host1280000000000
[zk: host128:2181(CONNECTED) 3] create -e -s /servers/host129 "hsot129"
Created /servers/host1290000000001
[zk: host128:2181(CONNECTED) 4] create -e -s /servers/host130 "hsot130"
Created /servers/host1300000000002
5.4.1.3.观察Idea 控制台变化
[]
[]
[hsot128]
[hsot129, hsot128]
[hsot130, hsot129, hsot128]
5.4.1.4.执行删除操作
[zk: host128:2181(CONNECTED) 6] ls /servers
[host1280000000000, host1290000000001, host1300000000002]
[zk: host128:2181(CONNECTED) 7] delete /servers/host1280000000000
[zk: host128:2181(CONNECTED) 8] delete /servers/host1290000000001
5.4.1.5.观察Idea 控制台变化
[hsot130, hsot129]
[hsot130]

5.4.2.在Idea 上操作增加减少服务器

5.4.2.1.启动DistributeClient 客户端(如果已经启动过,不需要重启)
5.4.2.2.启动DistributeServer 服务
5.4.2.2.1.点击Edit Configurations…

在这里插入图片描述

5.4.2.2.2.在弹出的窗口中(Program arguments)输入想启动的主机,例如,host130

在这里插入图片描述

5.4.2.2.3.回到DistributeServer的main方法,右 键,在弹出的窗口中点击 Run “DistributeServer.main()”

在这里插入图片描述

5.4.2.2.4.观察DistributeServer 控制台
host130 is online
5.4.2.2.5.观察DistributeClient 控制台
#host130 已经上线
[hsot130]

六、ZooKeeper 分布式锁案例

6.1.分布式锁

比如说"进程 1"在使用该资源的时候,会先去获得锁,"进程 1"获得锁以后会对该资源保持独占,这样其他进程就无法访问该资源,"进程1"用完该资源以后就将锁释放掉,让其他进程来获得锁,那么通过这个锁机制,我们就能保证了分布式系统中多个进程能够有序的访问该临界资源。那么我们把这个分布式环境下的这个锁叫作分布式锁

在这里插入图片描述

6.2.ZooKeeper分布式锁原理

核心思想:当客户端要获取锁,则创建节点,使用完锁,则删除该节点。
    1. 客户端获取锁时,在lock节点下创建临时顺序节点
    1. 然后获取lock下面的所有子节点,客户端获取到所有的子节点之后,如果发现自己创建的子节点序号最小,那么就认为该客户端获取到了锁。使用完锁后,将该节点删除
    1. 如果发现自己创建的节点并非lock所有子节点中最小的,说明自己还没有获取到锁,此时客户端需要找到比自己小的那个节点,同时对其注册事件监听器监听删除事件
    1. 如果发现比自己小的那个节点被删除,则客户端的Watcher会收到相应通知,此时再次判断自己创建的节点是否是lock子节点中序号最小的,如果是则获取到了锁,如果不是则重复以上步骤继续获取到比自己小的一个节点并注册监听。

在这里插入图片描述

6.3.分布式锁案例分析

在这里插入图片描述

1)接收到请求后,在/locks节点下创建一个临时顺序节点

2)判断自己是不是当前节点下最小的节点:是,获取到锁;不是,对前一个节点进行监听

3)获取到锁,处理完业务后,delete节点释放锁,然后下面的节点将收到通知,重复第二步判断

6.3.原生Zookeeper 实现分布式锁案例

6.3.1.分布式锁实现

package com.orange.zkcase2;import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;/*** Description: zookeeper分布式锁案例*/
public class DistributedLock {//注意:connectString逗号左右不能有空格,否则连接不上private final String connectString = "192.168.147.128:2181,192.168.147.129:2181,192.168.147.130:2181";//tickTime为2000,initLimit为10//LF初次连接时的超时时间应起码大于延迟时间tickTime*initLimit的值否则会因为超时而连接失败。private final int sessionTimeout = 200000;private final ZooKeeper zkClient;//增加代码健壮性//zookeeper连接private CountDownLatch connectLatch = new CountDownLatch(1);//zookeeper等待private CountDownLatch waitLatch = new CountDownLatch(1);//当前client等待的子节点的路径private String waitPath;//当前client创建的子节点private String currentNode;/*** 和zk创建连接,并创建根节点*/public DistributedLock() throws IOException, InterruptedException, KeeperException {//1.获取连接 建立服务端与客户端连接zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {@Overridepublic void process(WatchedEvent watchedEvent) {System.out.println("-----process-------");// connectLatch 如果连接上zk  可以释放// 连接建立时, 打开latch, 唤醒wait在该latch上的线程if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {connectLatch.countDown();}// 发生了waitPath的删除事件 需要释放if (watchedEvent.getType() == Event.EventType.NodeDeleted && watchedEvent.getPath().equals(waitPath)) {waitLatch.countDown();}}});//等待zookeeper正常连接后,代码才往下继续执行connectLatch.await();//2.判断根节点 /locks 是否存在Stat stat = zkClient.exists("/locks", false);//如果根节点不存在,则创建根节点,根节点类型为永久节点if (stat == null) {System.out.println("根节点不存在");// 创建根节点,根节点必须是永久节点zkClient.create("/locks", "locks".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);}}//对zk 加锁public void zkLock() {try {//创建对应的临时带序号临时节点,返回值为创建的节点路径currentNode = zkClient.create("/locks/" + "seq-", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);System.out.println(Thread.currentThread().getName() + "当前节点为:" + currentNode);//注意, 没有必要监听"/locks"的子节点的变化情况//判断创建的节点是否是最小的序号节点,如果是获取到锁;如果不是,监听它序号前一个节点List<String> children = zkClient.getChildren("/locks", false);//如果children只要一个子节点,那就直接获取锁; 如果有多个节点,需要判断,谁最小if (children.size() == 1) {System.out.println(Thread.currentThread().getName() + "对zk 加锁, 当前节点:" + currentNode);return;} else {///对根节点下的所有临时顺序节点进行从小到大排序,有序递增Collections.sort(children);//获取当前节点名称 seq-00000000String thisNode = currentNode.substring("/locks/".length());System.out.println(Thread.currentThread().getName() + "当前节点名称为:" + thisNode);// 通过seq-00000000 获取该节点在children集合的位置int index = children.indexOf(thisNode);System.out.println(Thread.currentThread().getName() + "当前节点在集合的位置为:" + index);//判断if (index == -1) {System.out.println(Thread.currentThread().getName() + "数据异常");} else if (index == 0) {//只有一个节点,就可以获取锁了System.out.println(Thread.currentThread().getName() + "对zk 加锁, 当前节点:" + currentNode);return;} else {//获得排名比 currentNode 前 1 位的节点waitPath = "/locks/" + children.get(index - 1);System.out.println(Thread.currentThread().getName() + "前一个节点为:" + waitPath);//在 waitPath 上注册监听器, 当 waitPath 被删除时, zookeeper会回调监听器的 process方法//需要监听 它前一个节点变化zkClient.getData(waitPath, true, null);//入等待锁状态,等待监听waitLatch.await();return;}}} catch (KeeperException e) {throw new RuntimeException(e);} catch (InterruptedException e) {throw new RuntimeException(e);}}//对zk 解锁public void unZkLock() {try {System.out.println(Thread.currentThread().getName() + "解锁,删除当前节点:" + currentNode);//删除节点zkClient.delete(currentNode, -1);} catch (InterruptedException e) {throw new RuntimeException(e);} catch (KeeperException e) {throw new RuntimeException(e);}}
}

6.3.2.分布式锁测试

6.3.2.1.创建两个线程
package com.orange.zkcase2;import org.apache.zookeeper.KeeperException;import java.io.IOException;/*** Description: 测试分布式锁*/
public class DistributedLockTest {public static void main(String[] args) throws IOException, InterruptedException, KeeperException {// 创建分布式锁// final修饰的对象必须被初始化,不能被修改。// 非final的对象可以被重新赋值,锁对象就不受管控了。// 当一个锁被其他对象占有时,当前线程可以对锁对象重新赋值(相当于从新创建了一个锁对象),从而也拿到了运行的权利。//创建分布式锁 1final DistributedLock lock1 = new DistributedLock();//创建分布式锁 2final DistributedLock lock2 = new DistributedLock();new Thread(new Runnable() {@Overridepublic void run() {//获取锁对象try {lock1.zkLock();System.out.println("线程0 启动,获取到锁");Thread.sleep(5*1000);//延迟5秒lock1.unZkLock();System.out.println("线程0 释放锁");} catch (InterruptedException e) {throw new RuntimeException(e);}}}).start();new Thread(new Runnable() {@Overridepublic void run() {//获取锁对象try {lock2.zkLock();System.out.println("线程1 启动,获取到锁");Thread.sleep(5 * 1000);//延迟5秒lock2.unZkLock();System.out.println("线程1 释放锁");} catch (InterruptedException e) {throw new RuntimeException(e);}}}).start();}
}
6.3.2.2.观察控制台变化
-----process-------
根节点不存在-----process-------
Thread-1当前节点为:/locks/seq-0000000000
Thread-0当前节点为:/locks/seq-0000000001
Thread-1当前节点名称为:seq-0000000000
Thread-1当前节点在集合的位置为:0
Thread-1对zk 加锁, 当前节点:/locks/seq-0000000000
线程1 启动,获取到锁
Thread-0当前节点名称为:seq-0000000001
Thread-0当前节点在集合的位置为:1
Thread-0前一个节点为:/locks/seq-0000000000
Thread-1解锁,删除当前节点:/locks/seq-0000000000
-----process-------
线程0 启动,获取到锁
线程1 释放锁
Thread-0解锁,删除当前节点:/locks/seq-0000000001
线程0 释放锁

6.4.Curator框架实现分布式锁案例

6.4.1.Curator有五种锁方案:

  • InterProcessSemaphoreMutex:分布式排它锁(非可重入锁)
  • InterProcessMutex:分布式可重入排它锁
  • InterProcessReadWriteLock:分布式读写锁
  • InterProcessMultiLock:将多个锁作为单个实体管理的容器
  • InterProcessSemaphoreV2:共享信号量

6.4.1.原生的Java API 开发存在的问题

(1)会话连接是异步的,需要自己去处理。比如使用 CountDownLatch

(2)Watch 需要重复注册,不然就不能生效

(3)开发的复杂性还是比较高的

(4)不支持多节点删除和创建。需要自己去递归

6.4.2.Curator是一个专门解决分布式锁的框架,解决了原生Java API开发分布式遇到的问题

官方文档:https://curator.apache.org/index.html

6.4.3.Curator 案例实操

6.4.3.1.添加依赖
    <dependency><groupId>org.apache.curator</groupId><artifactId>curator-framework</artifactId><version>4.3.0</version></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>4.3.0</version></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-client</artifactId><version>4.3.0</version></dependency>
6.4.3.2.代码实现
package com.orange.zkcase3;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;/*** Description: Curator 框架实现分布式锁案例*/
public class CuratorLockTest {public static void main(String[] args) {//创建分布式锁1InterProcessMutex lock1 = new InterProcessMutex(getCuratorFramework(), "/locks");//创建分布式锁2InterProcessMutex lock2 = new InterProcessMutex(getCuratorFramework(), "/locks");new Thread(new Runnable() {@Overridepublic void run() {try {//获取到锁lock1.acquire();System.out.println("线程1 获取到锁");//测试锁重入lock1.acquire();System.out.println("线程1 再次获取到锁");Thread.sleep(3 * 1000);//释放锁lock1.release();System.out.println("线程1 释放锁");lock1.release();System.out.println("线程1 再次释放锁");} catch (Exception e) {throw new RuntimeException(e);}}}).start();new Thread(new Runnable() {@Overridepublic void run() {try {//获取到锁lock2.acquire();System.out.println("线程2 获取到锁");//测试锁重入lock2.acquire();System.out.println("线程2 再次获取到锁");Thread.sleep(3 * 1000);//释放锁lock2.release();System.out.println("线程2 释放锁");lock2.release();System.out.println("线程2 再次释放锁");} catch (Exception e) {throw new RuntimeException(e);}}}).start();}/*** 分布式锁初始化*/private static CuratorFramework getCuratorFramework() {//重试策略,初试时间 3秒,重试3次ExponentialBackoffRetry policy = new ExponentialBackoffRetry(3000, 3);//通过工厂创建CuratorCuratorFramework client = CuratorFrameworkFactory.builder()//zookeeper server列表.connectString("192.168.147.128:2181,192.168.147.129:2181,192.168.147.130:2181")//connection超时时间.connectionTimeoutMs(20000)//session超时时间.sessionTimeoutMs(20000).retryPolicy(policy).build();//启动客户端client.start();System.out.println("zookeeper 初始化完成...");return client;}
}
6.4.3.3.控制台变化
zookeeper 初始化完成...zookeeper 初始化完成...线程1 获取到锁
线程1 再次获取到锁
线程1 释放锁
线程1 再次释放锁
线程2 获取到锁
线程2 再次获取到锁
线程2 释放锁
线程2 再次释放锁

七、模拟12306售票案例

7.1.代码实现

/*** Description: 模拟12306售票案例*/
public class LockTicket implements Runnable {private int tickets = 20;//数据库的票数private InterProcessMutex lock;public LockTicket() {//重试策略,初试时间 3秒,重试3次ExponentialBackoffRetry policy = new ExponentialBackoffRetry(3000, 3);//通过工厂创建client客户端对象CuratorFramework client = CuratorFrameworkFactory.builder()//zookeeper server列表.connectString("192.168.147.128:2181,192.168.147.129:2181,192.168.147.130:2181")//connection超时时间.connectionTimeoutMs(20000)//session超时时间.sessionTimeoutMs(20000).retryPolicy(policy).build();//启动客户端client.start();lock = new InterProcessMutex(client, "/locks");}@Overridepublic void run() {while (true) {try {//获取锁lock.acquire(3, TimeUnit.SECONDS);if (tickets > 0) {Thread.sleep(1000);System.out.println(Thread.currentThread().getName() + ":" + tickets);tickets--;}} catch (Exception e) {e.printStackTrace();} finally {//释放锁try {lock.release();} catch (Exception e) {e.printStackTrace();}}}}
}

7.2.测试

/*** Description: 模拟12306售票案例*/
public class LockTicketTest {public static void main(String[] args) {LockTicket lockTicket=new LockTicket();//创建客户端Thread t1 = new Thread(lockTicket, "携程");Thread t2 = new Thread(lockTicket, "飞猪");t1.start();t2.start();}
}

7.3.控制台变化

飞猪:20
携程:19
飞猪:18
携程:17
飞猪:16
携程:15
飞猪:14
携程:13
飞猪:12
携程:11
飞猪:10
携程:9
飞猪:8
携程:7
飞猪:6
携程:5
飞猪:4
携程:3
飞猪:2
携程:1

八、企业面试真题(面试重点)

8.1.选举机制

半数机制,超过半数的投票通过,即通过。

(1)第一次启动选举规则: 投票过半数时,服务器 id 大的胜出
(2)第二次启动选举规则:

  • EPOCH 大的直接胜出
  • EPOCH 相同,事务 id 大的胜出
  • 事务 id 相同,服务器 id 大的胜出

8.2.生产集群安装多少zk 合适

安装奇数台
生产经验

  • 10 台服务器:3 台zk
  • 20 台服务器:5 台zk
  • 100 台服务器:11 台zk
  • 200 台服务器:11 台zk

服务器台数多:好处,提高可靠性;坏处:提高通信延时

8.3.常用命令

ls、get、create、delete 

endl

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/708737.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Neoverse S3 系统 IP:机密计算和多芯片基础设施 SoC 的基础

第三代Neoverse系统IP Neoverse S3 产品推出了我们的第三代基础设施特定系统 IP&#xff0c;这是下一代基础设施 SOC 的理想基础&#xff0c;适用于从 HPC 和机器学习到 Edge 和 DPU 的各种应用。S3 机箱专注于为我们的合作伙伴提供 Chiplet、机密计算等关键创新以及 UCIe、DD…

(Linux学习一):Mac安装vmWare11.5,centOS 7安装步骤教程

一。下载vmware 官网地址&#xff1a;下载地址 由于我的电脑系统是Mac 10.15.6版本系统&#xff0c;我下载的是VMware Fusion 11.5版本&#xff0c;13是最新版本不支持安装需要系统在11以上。 百度网盘下载地址: VMware Fusion 11 VMware Fusion 12 VMware Fusion 13 下载需要…

matlab实现不同窗滤波器示例

1 汉明窗低通滤波器 &#xff1a; 在Matlab中使用汉明窗设计低通滤波器可以通过fir1函数实现。汉明窗通常用于设计滤波器&#xff0c;可以提供更突出的频率特性。 下面是一个示例代码&#xff0c;演示如何在Matlab中使用汉明窗设计低通滤波器&#xff1a; % 定义滤波器参数 fs …

揭秘数字证书:保护你的数据不止于表面

数字证书&#xff0c;这个看似枯燥无味的电子文件&#xff0c;其实背后隐藏着一套精密的运行机制。今天陕西CA就来给大家揭开它的神秘面纱。 首先&#xff0c;数字证书是由权威的第三方机构颁发的&#xff0c;这些机构通常被称为证书颁发机构&#xff08;CA&#xff09;&#…

python web框架fastapi模板渲染--Jinja2使用技巧总结

文章目录 1.jinja2模板1.1、jinja2 的变量1.1.1 列表类型数据渲染1.1.2 字典类型数据渲染 2. jinja2 的过滤器3. jinja2 的控制结构3.1、分支控制3.2、循环控制 1.jinja2模板 要了解jinja2&#xff0c;那么需要先理解模板的概念。模板在Python的web开发中⼴泛使⽤&#xff0c;…

双硬盘备份的一种可行方案

双硬盘备份有什么优势弊端&#xff1f; 事物总有两面性&#xff0c;那么对于双硬盘数据备份任务来说&#xff0c;有什么优势与弊端呢&#xff1f; ◉ 双硬盘备份的优势&#xff1a; 安全性更好&#xff1a;由于数据备份到两个不同的硬盘&#xff0c;所以可以保证备份数据的冗…

基于springboot实现图书馆管理系统项目【项目源码+论文说明】

基于springboot实现图书馆管理系统演示 摘要 电脑的出现是一个时代的进步&#xff0c;不仅仅帮助人们解决了一些数学上的难题&#xff0c;如今电脑的出现&#xff0c;更加方便了人们在工作和生活中对于一些事物的处理。应用的越来越广泛&#xff0c;通过互联网我们可以更方便地…

练习 1 Web EasySQL极客大挑战

CTF Week 1 EasySQL极客大挑战 BUUCTF 典中典复习 Web SQL 先尝试输入&#xff0c;找一找交互页面 check.php 尝试万能语句 a’ or true SQL注入&#xff1a;#和–的作用 get传参只能是url编码&#xff0c;注意修改编码&#xff0c;输入的字符串要改成url格式。 POST请求和…

Linux:Kubernetes(k8s)——基础理论笔记(1)

我笔记来源的图片以及共享至GitHub&#xff0c;本章纯理论。这是k8s中部分的基础理论 &#x1f447; KALItarro/k8spdf: 这个里面只有一个pdf文件 (github.com)https://github.com/KALItarro/k8spdf&#x1f446; 什么是kubernetes kubernetes 是一个开源的&#xff0c;用于管…

Unity(第十一部)场景

游戏有多个场景组成&#xff08;新手村&#xff0c;某某副本&#xff0c;主城&#xff09; 场景是有多个物体组成&#xff08;怪物&#xff0c;地形&#xff0c;玩家等&#xff09; 物体是有多个组件组成&#xff08;刚体组件&#xff0c;自定义脚本&#xff09; 创建场景 编辑…

Intel SGX 概述 --潦草笔记

文章目录 前言一、SGX介绍1.1 指令介绍1.2 数据结构 二、内存保护过程2.1 enclave页面缓存&#xff08;EPC&#xff09;2.2 Enclave页面缓存映射&#xff08;EPCM&#xff09; 三、部署SGX参考资料 前言 SGX是Intel开发的新的处理器技术&#xff0c;可以在计算平台上提供一个可…

华为s5720s-28p-power-li-ac堆叠配置

叠物理约束&#xff1a; • 连线推荐示意图选用产品子系列中固定的一款设备做示例&#xff0c;与选择产品时指定型号的外观可能不同。示意图主要用于让用户了解相同子系列设备可以用作堆叠的端口的位置&#xff0c;以及使用不同的连线方式时如何连接设备上的端口。因此&#xf…

Spring 类型转换、数值绑定与验证(三)— Formatting 与 Validation

1 Formatting 在Spring中用于格式化数据及根据地域展示不同格式的数据。 图 Formatting接口 UML 1.1 注解驱动Formatting 自定义像“DateTimeFormat”注解来对相关字段格式化的步骤为&#xff1a; 自定义注解。定义一个实现AnnotationFormatterFactory接口的工厂类。往容器…

向日葵、Todesk、teamviewer等工具远程连接电脑时第三方应用显示白屏

问题描述&#xff1a;用向日葵远程等桌面时&#xff0c;当把显示器断电或者就没有显示器时或者笔记本盖子合住时&#xff0c;第三方软件显示白屏或显示不出来的问题。 原因&#xff1a;某些显卡在断开屏幕时自动降为低功耗模式。 解决 1、下载工具 https://www.amyuni.com/d…

NoSQL--虚拟机网络配置

目录 1.初识NoSQL 1.1 NoSQL之虚拟机网络配置 1.1.1 首先&#xff0c;导入预先配置好的NoSQL版本到VMware Workstation中 1.1.2 开启虚拟机操作&#xff1a; 1.1.2.1 点击开启虚拟机&#xff1a; 1.1.2.2 默认选择回车CentOS Linux&#xff08;3.10.0-1127.e17.x86_64) 7 …

动态规划之使用最小花费爬楼梯【LeetCode】

动态规划之使用最小花费爬楼梯 LCR 088. 使用最小花费爬楼梯解法1解法2 LCR 088. 使用最小花费爬楼梯 LCR 088. 使用最小花费爬楼梯 解法1 状态表示&#xff08;这是最重要的&#xff09;&#xff1a;dp[i]表示以第i级台阶为楼层顶部&#xff0c;到达第i层台阶的最低花费。 状…

【VSCode】解决VSCode远程连接问题:远程主机可能不符合 glibc 和 libstdc++

今天用VSCode进行ssh连接时&#xff0c;提示“远程主机可能不符合 glibc 和 libstdc VSCode 服务器的先决条件”。查了一下发现这个问题主要是由于VSCode在一月份发布的最新版本v1.86中要求远程主机 glibc>2.28导致的&#xff0c;所以ssh连接Ubuntu 18.04的时候就会提示这个…

详解 Rope (Opal-03a) 的变化

文章目录 &#xff08;一&#xff09;特点&#xff08;二&#xff09;使用流程&#xff08;三&#xff09;界面&#xff08;四&#xff09;详解&#xff08;4.1&#xff09;目录区域⭐Start Rope⭐Video Folder⭐Output Folder⭐Faces Folder &#xff08;4.2&#xff09;预览控…

Python爬虫项目实战案例-批量下载网易云榜单音乐保存至本地

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua&#xff0c;在这里我会分享我的知识和经验。&#x…

汽车后视镜反射率检测仪厂家

随着汽车工业的快速发展&#xff0c;汽车后视镜作为驾驶员观察车辆周围环境的重要工具&#xff0c;其性能和质量对于交通安全至关重要。汽车后视镜的反射率检测仪是一种用于检测汽车后视镜反射性能的专业设备&#xff0c;其重要性不言而喻。本文将重点介绍汽车后视镜反射率检测…