01-ZooKeeper-Schnellstart

1 Zookeeper-Konzept

Zookeeper ist ein Unterprojekt des Apache Hadoop-Projekts und ein Baumverzeichnisdienst .

Zookeeper heißt übersetzt Zookeeper und ist der Administrator, der zur Verwaltung von Hadoop (Elefant), Hive (Biene) und Pig (Schwein) verwendet wird. Kurz ZK

Zookeeper ist ein verteiltes Open-Source- Apache-Projekt, das Dienste für verteilte Anwendungen koordiniert .

Zu den Hauptfunktionen von Zookeeper gehören:

  • ​ Konfigurationsmanagement (als Konfigurationscenter)
  • ​ Verteilte Sperre
  • ​ Clustermanagement (als Registrierungsstelle)

Zookeeper-Datenstruktur

Die Struktur des Zookeeper-Datenmodells ist dem Unix-Dateisystem sehr ähnlich. Es kann als Baum als Ganzes betrachtet werden, und jeder Knoten wird als ZNode bezeichnet. Jeder ZNode kann standardmäßig 1 MB Daten speichern und jeder ZNode kann anhand seines Pfads eindeutig identifiziert werden.

1.1 Zookeeper-Anwendungsszenarien

Zu den bereitgestellten Diensten gehören: einheitlicher Namensdienst, einheitliches Konfigurationsmanagement, einheitliche Clusterverwaltung, dynamische Online- und Offline-Serverknoten, sanfter Lastausgleich usw.

1) Einheitlicher Namensdienst

In einer verteilten Umgebung ist es häufig erforderlich, Anwendungen/Dienste zur einfachen Identifizierung einheitlich zu benennen.

Beispiel: Eine IP ist nicht leicht zu merken, ein Domänenname jedoch leicht zu merken

2) Einheitliches Konfigurationsmanagement

(1) In einer verteilten Umgebung ist die Synchronisierung von Konfigurationsdateien sehr häufig.

1. Im Allgemeinen ist es erforderlich, dass die Konfigurationsinformationen aller Knoten in einem Cluster konsistent sind, beispielsweise in einem Kafka-Cluster.

2. Nachdem wir die Konfigurationsdatei geändert haben, hoffen wir, sie schnell mit jedem Knoten synchronisieren zu können.

(2) Das Konfigurationsmanagement kann von zookeeper implementiert werden

1. Konfigurationsinformationen können in einen ZNode auf zookeeper geschrieben werden

2. Jeder Client-Server überwacht diesen ZNode

3. Sobald die Daten im ZNode geändert werden, benachrichtigt Zookeeper jeden Client-Server

3) Einheitliche Clusterverwaltung

(1) In einer verteilten Umgebung ist es notwendig, den Status jedes Knotens in Echtzeit zu erfassen.

1. Je nach Echtzeitstatus des Knotens können einige Anpassungen vorgenommen werden.

(2) Zookeeper kann Knotenstatusänderungen überwachen

1. Knoteninformationen können in einen ZNode in Zookeeper geschrieben werden

2. Überwachen Sie diesen ZNode, um seine Statusänderungen in Echtzeit zu erhalten

4) Server dynamisch online und offline

Der Client kann in Echtzeit Einblick in die Upstream- und Downstream-Änderungen des Servers erhalten

​ (1) Registrieren Sie die Informationen beim Serverstart (alle temporären Knoten werden erstellt)

​ (2) Besorgen Sie sich die aktuelle Online-Serverliste und registrieren Sie sich für die Überwachung

​ (3) Wenn der Serverknoten offline geht

(4) Benachrichtigung darüber, dass der Serverknoten online und offline geht

​ (5) Rufen Sie die Serverliste erneut ab und registrieren Sie sich für die Überwachung

5) Sanfter Lastausgleich

Zeichnen Sie die Anzahl der Besuche auf jedem Server in Zookeeper auf und lassen Sie den Server mit der geringsten Anzahl von Besuchen die neuesten Clientanforderungen verarbeiten.

1.2 Zookeeper-Arbeitsmechanismus

Zookeeper wird aus der Perspektive des Entwurfsmusters verstanden: Es handelt sich um ein verteiltes Service-Management-Framework, das auf dem Beobachtermuster basiert. Es ist für die Speicherung und Verwaltung von Daten verantwortlich, die allen wichtig sind, und akzeptiert dann die Registrierung von Beobachtern . Sobald der Status dieser festgelegt ist Datenänderungen, zookeeper Es ist dafür verantwortlich, zu benachrichtigen, welche Beobachter bei zookeeper registriert wurden, um entsprechend zu reagieren.

1.3 Designzweck von Zookeeper

Der Gestaltungszweck spiegelt sich hauptsächlich in den folgenden Aspekten wider:

1) Konsistenz: Unabhängig davon, mit welchem ​​Server sich der Client verbindet, sieht er oder sie die gleiche Ansicht.

2) Echtzeit: Zookeeper-Daten werden im Speicher gespeichert, wodurch ein hoher Durchsatz und eine geringe Latenz erreicht werden können.

3) Zuverlässigkeit: Die Server, aus denen der Zookeeper-Dienst besteht, müssen sich gegenseitig über die Existenz anderer Server informieren

4) Ordnung: Zookeeper weist beispielsweise jedem Aktualisierungsvorgang eine Versionsnummer zu. Diese Versionsnummer ist eindeutig und geordnet.

5) Atomarität: Wenn der Zookeeper-Client Daten liest, kann er nur zwei Zustände haben: Erfolg oder Fehler, und es gibt keine Situation, in der nur ein Teil der Daten gelesen wird (dh der Vorgang wird nicht unterbrochen, weder erfolgreich noch erfolgreich). fehlgeschlagen)

1.4 Zookeeper-Systemmodell

Das Systemmodell von Zookeeper umfasst Server und Client

1) Der Client kann eine Verbindung zu jedem Server im Zookeeper-Cluster herstellen. Der Client und der Server stellen eine Verbindung über TCP her und sind hauptsächlich für das Senden von Anforderungen und Heartbeat-Nachrichten, das Erhalten von Antworten und die Überwachung von Ereignissen verantwortlich.

2) Wenn die TCP-Verbindung zwischen Client und Server unterbrochen wird, versucht der Client automatisch, eine Verbindung zu anderen Servern herzustellen

3) Wenn ein Client zum ersten Mal eine Verbindung zu einem Server herstellt, baut der Server eine Sitzung für den Client auf. Wenn der Client eine Verbindung zu einem anderen Server herstellt, stellt der neue Server eine Sitzung für den Client wieder her

1.5 Zookeeper-Clusterrolle

Es gibt drei Rollen im Zookeeper-Clusterdienst

Standardmäßig gibt es einen Anführer (Leader) und einen Cluster bestehend aus mehreren Followern (Follower).

Anführer Anführer:

1. Transaktionsanfragen bearbeiten

2. Scheduler jedes Servers innerhalb des Clusters

Follower Follower:

1. Verarbeiten Sie Nicht-Transaktionsanfragen des Clients und leiten Sie Transaktionsanfragen an den führenden Server weiter

2. Nehmen Sie an der Abstimmung zur Führungswahl teil (Wahl basiert auf der Größe der Server-ID).

Beobachter Beobachter:

1. Verarbeiten Sie Nicht-Transaktionsanfragen des Kunden und geben Sie Transaktionsanfragen an Nicht-Führungskräfte aus

2. Beteiligen Sie sich nicht an der Abstimmung zur Wahl des Vorsitzenden

Beobachter ist eine neue Rolle ab der Version zookeeper3.3.0. Wenn die Anzahl der Knoten in einem einzelnen Zookeeper-Cluster zunimmt, müssen mehr Server hinzugefügt werden, um mehr Clients zu unterstützen. Mit zunehmender Anzahl von Servern nimmt die Abstimmungsphase jedoch zu viel Zeit in Anspruch, was sich auf die Clusterleistung auswirkt. Um die Skalierbarkeit des Clusters zu verbessern und einen hohen Datendurchsatz sicherzustellen, wird Observer eingeführt

1.6 Zookeeper-Funktionen

  • Solange mehr als die Hälfte der Knoten im Cluster überleben, kann der Zookeeper-Cluster normal funktionieren. Daher eignet sich zookeeper für die Installation einer ungeraden Anzahl von Servern.

  • Globale Datenkonsistenz**: Jeder Server speichert eine identische Kopie der Daten**. Unabhängig davon, mit welchem ​​Server sich der Client verbindet, sind die Daten konsistent.

  • Aktualisierungsanforderungen werden nacheinander ausgeführt , und Aktualisierungsanforderungen desselben Clients werden nacheinander in der Reihenfolge ihres Auftretens ausgeführt.

  • Atomizität der Datenaktualisierung : Eine Datenaktualisierung ist entweder erfolgreich oder schlägt fehl.

  • Echtzeit : Innerhalb eines bestimmten Zeitraums kann der Client die neuesten Daten lesen

2 Bau des Tierpflegerdienstes

Offizielle Website

https://zookeeper.apache.org

2.1 Zookeeper-Standalone-Modus

1) Vorbereitung der Umgebung

Der Zookeeper-Server wird in Java erstellt, er läuft auf der JVM und muss auf JDK7 oder höher installiert werden.

2) Hochladen und dekomprimieren

Laden Sie das heruntergeladene komprimierte Zookeeper-Paket in das Verzeichnis /opt/software** hoch (alle folgenden Vorgänge basieren auf dem Root-Benutzer**)

[root@kk01 software]# cd /opt/software/
# 上传 
[root@kk01 software]# rz
# 解压
[root@kk01 software]# tar -zxvf apache-zookeeper-3.6.1-bin.tar.gz
# 删除压缩包
[root@kk01 software]# rm -rf apache-zookeeper-3.6.1-bin.tar.gz 
# 将解压后的目录重命名
[root@kk01 software]# mv  apache-zookeeper-3.6.1-bin zookeeper-3.6.1

3) Konfigurieren Sie zoo.cfg

Benennen Sie die Datei zoo_sample.cfg im Verzeichnis /opt/software/zookeeper/apache-zookeeper-3.6.1-bin/conf in zoo.cfg um

[root@kk01 software]# cd zookeeper-3.6.1/conf/
# mv重命名 也可以使用拷贝更名(cp)
[root@kk01 conf]# mv zoo_sample.cfg zoo.cfg

# 在/opt/softeware/ zookeeper-3.6.1 目录下创建zookeeper存储目录zkData
[root@kk01 conf]# cd /opt/software/zookeeper-3.6.1
[root@kk01 zookeeper-3.6.1]# mkdir zkData

# 进入zoo.cfg文件进行修改存储目录
[root@kk01 zookeeper-3.6.1]# cd /opt/software/zookeeper-3.6.1/conf
[root@kk01 conf]# vim zoo.cfg

# 做出如下修改
dataDir=/opt/software/zookeeper-3.6.1/zkData

4) Konfigurieren Sie Zookeeper-Umgebungsvariablen

[root@kk01 conf]# vim /etc/profile

# 在文件末尾添加以下内容

# zookeeper env
export ZOOKEEPER_HOME=/opt/software/zookeeper-3.6.1
export PATH=$PATH:$ZOOKEEPER_HOME/bin
     
# 使环境变量生效
[root@kk01 conf]# source /etc/profile

5) Starten Sie Zookeeper

Die Umgebungsvariablen von Zookeeper sind konfiguriert, sodass das bin-Verzeichnis des Zookeepers nicht eingegeben werden muss.

# 若未配置zookeeper环境变量,先切换目录
[root@kk01 conf]# cd /opt/software/zookeeper-3.6.1/bin/
# 启动
./zkServer.sh start
[root@kk01 bin]# ./zkServer.sh start


# 配置了环境变量直接使用以下命令
zkServer.sh start  #全局可用

# 见到如下信息则说明zookeeper启动成功
Using config: /opt/software/zookeeper-3.6.1/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED

# 查看进程
[root@kk01 bin]# jps
2835 Jps
2764 QuorumPeerMain

Zookeeper-Start anzeigen

Es gibt zwei Möglichkeiten zu überprüfen, ob Zookeeper erfolgreich gestartet wurde.

1. Überprüfen Sie den Startstatus von Zookeeper

[root@kk01 bin]# zkServer.sh status   # 看到如下信息说明启动成功
ZooKeeper JMX enabled by default
Using config: /opt/software/zookeeper-3.6.1/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: standalone

# 看到如下信息说明没有启动成功
ZooKeeper JMX enabled by default
Using config: /opt/software/zookeeper/zookeeper-3.6.1/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Error contacting service. It is probably not running.

2. Verwenden Sie den Befehl jps, um den Zookeeper-Dienstprozess QuorumPeerMain anzuzeigen (dieser Prozess ist der Starteingang des Zookeeper-Clusters).

# 看到如下信息说明启动成功
[root@kk01 bin]# jps
2835 Jps
2764 QuorumPeerMain

2.1.1 Interpretation der Konfigurationsparameter

Die Bedeutung der Parameter in der Konfigurationsdatei zoo.cfg in Zookeeper ist wie folgt

# 通信心跳时间,zookeeper服务器与客户端心跳时间,单位毫秒 
tickTime=2000	
# LF初始通信时限,即Leader和Follwer初始连接时能容忍的最多心跳数(tickTime的数量)
initLimit=10	
# LF同步通信时限,即Leader和Follwer之间通信时间如果超过 syncLimit*tickTime,Leader认为Follwer死掉,从服务器列表中删除Follwer
syncLimit=5
# 保存zookeeper中的数据(默认为tmp目录,容易被Linux系统定期删除,所以一把不使用默认的tmp目录)
dataDir=/opt/software/zookeeper-3.6.1/zkdata
# 客户端连接端口,通常不做修改
clientPort=2181

2.2 Zookeeper vollständig verteilt

Einführung in den Zookeeper-Cluster

  • Wahl des Vorsitzenden :

Serverid: Server-ID

Beispielsweise gibt es drei Server mit den Nummern 1, 2 und 3. Je höher die Zahl, desto größer das Gewicht im Auswahlalgorithmus.

  • Zxid: Daten-ID

Die maximale Daten-ID, die auf dem Server gespeichert ist. Je größer der Wert, desto aktueller sind die Daten.

  • Wenn ein Tierpfleger während der Wahl des Anführers mehr als die Hälfte der Stimmen erhält, kann dieser Tierpfleger zum Anführer werden.

Was Sie über das Bauen wissen müssen

Echte Cluster werden auf verschiedenen Servern bereitgestellt. Hier simulieren wir mithilfe virtueller Maschinen drei Server, um eine vollständige Verteilung zu simulieren . Natürlich können Sie auch eine virtuelle Maschine verwenden, um einen pseudoverteilten Cluster aufzubauen . Dann differenzieren Sie anhand des Ports .

Der Unterschied zwischen vollständig verteilt und pseudo-verteilt besteht darin, dass vollständig verteilt auf mehreren Maschinen aufgebaut ist und anhand der IP unterschieden wird. Pseudo-verteilt ist auf einer Maschine aufgebaut und wird anhand von IP und Port unterschieden.

Clusterplanung

​ Denn solange mehr als die Hälfte der Knoten im Cluster überleben, kann der Zookeeper-Cluster normal funktionieren, daher verwenden wir hier drei Server zur Demonstration.

kk01	192.168.188.128
kk02	192.168.188.129
kk03    192.168.188.130

1) Vorbereitungsarbeiten

Installieren Sie JDK und Zookeeper und laden Sie sie auf den Server hoch (diese wurden im Standalone-Modus implementiert und werden hier weggelassen).

2) Erstellen Sie eine myid-Datei mit dem Inhalt 1 in /opt/software/zookeeper-3.6.1/zkData.

In dieser Datei wird die ID jedes Servers aufgezeichnet (myid muss eine Ganzzahl von 1 bis 255 sein und der Inhalt in myid muss im Cluster eindeutig sein).

[root@kk01 zkData]# cd /opt/software/zookeeper-3.6.1/zkData
[root@kk01 zkData]# echo 1 > ./myid

3) Ändern Sie die Datei zoo.cfg im Verzeichnis /opt/software/zookeeper-3.6.1/conf

[root@kk01 zkData]# vi /opt/software/zookeeper-3.6.1/conf/zoo.cfg

# 在文件末尾添加如下内容
server.1=192.168.188.128:2881:3881
server.2=192.168.188.129:2881:3881
server.3=192.168.188.130:2881:3881

# 2881是Leader端口,负责和Follower进行通信。3881是Follower端口,进行推选Leader

# 配置参数解读
# 	server.服务器ID=服务器IP地址:服务器之间通信端口:服务器之间投票选举端口
# 	服务器ID,即配置在zkData目录下myid文件里的值
#		zk启动时读取此文件,拿到里面的数据与zoo.cfg里的配置信息对比,从而判断到底是哪个server
#	服务器IP地址
#	服务器之间通信端口,服务器Follwer与集群中的Leader服务器交换信息的端口
#	服务器之间投票选举端口,是万一集群中的Leader服务器挂掉了,需要一个端口来重新选举出新的Leader,这个端口就是用来执行选举时服务器相互通信的端口

4) Verteilen Sie /opt/software/zookeeper-3.6.1/ an die virtuellen Maschinen kk02 und kk03

[root@kk01 zkData]# scp -r /opt/software/zookeeper-3.6.1/ root@kk03:/opt/software/zookeeper-3.6.1

[root@kk01 zkData]# scp -r /opt/software/zookeeper-3.6.1/ root@kk03:/opt/software/zookeeper-3.6.1

5) Ändern Sie die myid-Dateien im Verzeichnis /opt/software/zookeeper-3.6.1/zkData der virtuellen Maschinen kk02 bzw. kk03. Der Inhalt ist 2 bzw. 3.

# kk02
[root@kk02 ~]# cd /opt/software/zookeeper-3.6.1/zkData/
[root@kk02 zkData]# vi myid 

#kk03
[root@kk03 ~]# cd /opt/software/zookeeper-3.6.1/zkData/
[root@kk03 zkData]# vi myid 

6) Verteilen Sie die Umgebungskonfigurationsdatei der virtuellen Maschine kk01 an kk02 und kk03

[root@kk01 zkData]# scp -r /etc/profile root@kk02:/etc/profile
[root@kk01 zkData]# scp -r /etc/profile root@kk03:/etc/profile

# 分别在kk02、kk03下使用下面命令使环境变量生效
source /etc/profile

3) Verwenden Sie die folgenden Befehle, um den Zookeeper-Server auf kk01, kk02 bzw. kk03 zu starten.

[root@kk01 zkData]# zkServer.sh  start
[root@kk02 zkData]# zkServer.sh  start
[root@kk03 zkData]# zkServer.sh  start

3) Um den Zookeeper-Serverstatus von drei virtuellen Maschinen zu überprüfen , lautet der Befehl wie folgt

# 查看进程
[root@kk01 zkData]# jps
3440 QuorumPeerMain
3495 Jps

[root@kk02 zkData]# jps
2898 Jps
2844 QuorumPeerMain

[root@kk03 zkData]# jps
2855 Jps
2794 QuorumPeerMain


# 查看状态
[root@kk01 zkData]# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/software/zookeeper-3.6.1/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: leader

[root@kk02 zkData]# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/software/zookeeper-3.6.1/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: follower

[root@kk03 zkData]# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/software/zookeeper-3.6.1/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: follower

Die Überprüfung des Startstatus gibt die oben genannten Ergebnisse zurück. Modus: Follower Modus: Anführer wird angezeigt, was darauf hinweist, dass der Zookeeper-Cluster erfolgreich eingerichtet wurde.

2.2.1 Shell-Skript zum Starten und Stoppen des Zookeeper-Clusters mit einem Klick

Das Starten und Herunterfahren des Zookeeper-Clusters erfordert das Starten und Herunterfahren jeder virtuellen Maschine, was nicht effizient ist. In der tatsächlichen Arbeit werden viele Server verwendet. Um die Verwaltung des Zookeeper-Servers zu erleichtern, können Sie das Skript xzk.sh schreiben, um den Zookeeper-Servercluster mit einem Klick zu starten und zu stoppen.

Schreiben Sie im Verzeichnis /usr/local/bin der virtuellen Maschine kk01 die Skriptdatei xzk.sh. Der Dateiinhalt lautet wie folgt:

#!/bin/bash
cmd=$1
if [ $# -gt 1 ] ; then echo param must be 1 ; exit ; fi
for (( i=1 ; i <= 3; i++ )) ; do
        tput setaf 5
        echo ============ kk0$i $@ ============
        tput setaf 9
        ssh kk0$i "source /etc/profile ; zkServer.sh $cmd"
done

Fügen Sie dem Besitzer des xzk.sh-Skripts Ausführungsberechtigungen hinzu

chmod u+x xzk.sh
# 或
chmod 744 xzk.sh

Verwenden Sie die Start- und Stoppbefehle des xzk.sh-Skripts, um kk02 und kk03 auf kk01 gleichzeitig herunterzufahren

xzk.sh start
xzk.sh stop

Leichter verständliche Skripte

# 在虚拟机kk01的/usr/local/bin目录下 创建名为zk.sh的脚本

[root@kk01 ~]# cd /usr/local/bin/
[root@kk01 bin]# vim zk.sh

# 内容如下

#!/bin/bash

case $1 in
"start")
        for i in kk01 kk02 kk03
        do
                echo "----------------zookeeper $i start------------------------"
                ssh $i "/opt/software/zookeeper-3.6.1/bin/zkServer.sh $1"
        done
;;
"stop")
        for i in kk01 kk02 kk03
        do
                echo "----------------zookeeper $i stop------------------------"
                ssh $i "/opt/software/zookeeper-3.6.1/bin/zkServer.sh $1"
        done
;;
"status")
        for i in kk01 kk02 kk03
        do
                echo "----------------zookeeper $i status------------------------"
                ssh $i "/opt/software/zookeeper-3.6.1/bin/zkServer.sh $1"
        done
;;
*)
        echo "输入参数有误(请输入:start|stop|status)!"
esac



# 赋予文件可执行权限
[root@kk01 bin]# chmod u+x zk.sh 

Das obige Skript kann auf den folgenden Fehler stoßen

[root@kk01 bin]# pwd
/usr/local/bin   # 因为我们脚本放在该目录下,可能会遇到如下错误

[root@kk01 bin]# zk.sh status
----------------zookeeper kk01 status------------------------
Error: JAVA_HOME is not set and java could not be found in PATH.
----------------zookeeper kk02 status------------------------
Error: JAVA_HOME is not set and java could not be found in PATH.
----------------zookeeper kk03 status------------------------
Error: JAVA_HOME is not set and java could not be found in PATH.


# 解决方法
# 方法一:将自定义脚本放在家目录的bin目录下(我们采用root用户,所以需要放在 /root/bin/目录下)
[root@kk01 ~]# mkdir -p /root/bin
[root@kk01 ~]# cp /usr/local/bin/zk.sh /root/bin/
[root@kk01 bin]# ll
total 4
-rwxr--r--. 1 root root 615 Apr 20 00:17 zk.sh
# /root/bin目录添加到环境变量
[root@kk01 ~]# vim /etc/profile
# 内容如下

# 将root的bin目录添加到环境
export PATH=$PATH:/root/bin

[root@kk01 ~]# source /etc/profile


# 尽力了九九八十一难,最终脚本如下(上面脚本,错误的原因:我们再/etc/profile中配置了zookeeper环境变量,因此启动zkServer.sh 不再需要加路径)

#!/bin/bash

case $1 in
"start")
        for i in kk01 kk02 kk03
        do
                echo "----------------zookeeper $i start------------------------"
                ssh $i " source /etc/profile;  zkServer.sh $1"
        done
;;
"stop")
        for i in kk01 kk02 kk03
        do
                echo "----------------zookeeper $i stop------------------------"
                ssh $i " source /etc/profile; zkServer.sh $1"
        done
;;
"status")
        for i in kk01 kk02 kk03
        do
                echo "----------------zookeeper $i status------------------------"
                ssh $i " source /etc/profile; zkServer.sh $1"
        done
;;
*)
                echo '输入参数有误(请输入:start|stop|status)!'
esac

2.3 Cluster-Ausnahmesimulation (Wahlsimulation)

1) Testen Sie zunächst, was passiert, wenn der Server aufhängt

Stoppen Sie den Server auf kk03, beobachten Sie kk01 und kk02 und stellen Sie fest, dass es keine Änderung gibt.

# 在kk03上关闭zk
zkServer.sh stop

# 查看kk01   还是follower,无变化
[root@kk01 ~]# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/software/zookeeper/apache-zookeeper-3.6.1-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: follower

# 查看kk02   还是leader,无变化
[root@kk02 ~]# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/software/zookeeper/apache-zookeeper-3.6.1-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: leader

Daraus können wir schließen, dass in einem Cluster mit 3 Knoten der Cluster normal ist, wenn der Slave-Server auflegt.

2) Nachdem sich auch der Server auf kk01 aufgehängt hatte, überprüfte ich kk02 (den Standort des Hauptservers) und stellte fest, dass er ebenfalls nicht mehr lief.

# 在kk01上关闭zk
zkServer.sh stop

# 查看kk02   服务已停止
[root@kk02 ~]# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/software/zookeeper/apache-zookeeper-3.6.1-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Error contacting service. It is probably not running.

Daraus können wir schließen, dass in einem 3-Knoten-Cluster beide Slave-Server ausgefallen sind und der Hauptdienst nicht ausgeführt werden kann . Denn die Anzahl der lauffähigen Maschinen übersteigt nicht die Hälfte der Anzahl der Cluster.

3) Wir haben den Server auf kk01 erneut gestartet und festgestellt, dass der Server auf kk02 wieder normal funktionierte und immer noch führend war.

# 在kk01上开启zk
zkServer.sh start

# 查看kk02 	发现他又开始运行了,而且依然是领导者leader
[root@kk02 ~]# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/software/zookeeper/apache-zookeeper-3.6.1-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: leader

4) Wir starten den Server auf kk03, fahren den Server auf kk02 herunter und überprüfen den Status von kk01 und kk03

# 在kk03上开启zk
zkServer.sh start
# 在kk02上关闭zk
zkServer.sh stop

# 查看kk01	依然是follower
[root@kk01 ~]# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/software/zookeeper/apache-zookeeper-3.6.1-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: follower

# 查看kk03   变为了leader
[root@kk03 ~]# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/software/zookeeper/apache-zookeeper-3.6.1-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: leader

Es wurde festgestellt, dass ein neuer Anführer aufgetaucht ist

Daraus schließen wir, dass, wenn der Hauptserver im Cluster auflegt, andere Server im Cluster automatisch den Status auswählen und dann einen neuen Anführer generieren.

5) Was passiert, wenn Sie den Server auf kk02 erneut starten? Wird der Server auf kk02 wieder der neue Anführer?

# 在kk02上启动zk
zkServer.sh start

# 查看kk02
[root@kk02 ~]# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/software/zookeeper/apache-zookeeper-3.6.1-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.

# 查看kk03  依然是leader
[root@kk03 ~]# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/software/zookeeper/apache-zookeeper-3.6.1-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: leader

Der Server auf kk02 startet erneut und der Server auf kk03 bleibt weiterhin führend.

Daraus lässt sich schließen, dass der Spitzenreiter im zk-Cluster nicht nachgeben wird.

3 Zookeeper-Wahlmechanismus

Der Zookeeper-Wahlmechanismus ist ein Mechanismus mit halber Mehrheit. Wenn mehr als die Hälfte der Stimmen angenommen wird, wird er angenommen.

1) Starten Sie die Wahlregeln zum ersten Mal:

  • Bei mehr als der Hälfte der abgegebenen Stimmen gewinnt derjenige mit der größeren Server-ID.

2) Starten Sie die Wahlregeln zum zweiten Mal:

  • Derjenige mit der größeren EPOCHE (Punkt) gewinnt direkt.
  • EPOCH ist das gleiche, derjenige mit der größeren Transaktions-ID gewinnt.
  • Wenn die Transaktions-IDs gleich sind, gewinnt diejenige mit der größeren Server-ID.

Einzelheiten wie folgt:

3.1 Zookeeper-Wahlmechanismus (erster Start)

Jeder Schreibvorgang des Clients verfügt über eine Transaktions-ID (ZXID).

SID: Server-ID . Wird zur eindeutigen Identifizierung der Maschine verwendet, auf der sich ein Zookeeper-Cluster befindet. Jede Maschine kann nicht wiederholt werden und ist mit myid konsistent.

ZXID: Transaktions-ID. ZXID ist eine Transaktions-ID, die zur Identifizierung einer Änderung des Serverstatus verwendet wird . Zu einem bestimmten Zeitpunkt ist die ZXID jeder Maschine im Cluster möglicherweise nicht vollständig konsistent. Dies hängt mit der Verarbeitungslogik des Zookeeper-Servers für die „Aktualisierungsanforderung“ des Clients zusammen.

Epoche: Der Codename jedes Leader-Begriffs . Wenn es keinen Anführer gibt, ist der logische Uhrwert in derselben Abstimmungsrunde derselbe. Diese Daten erhöhen sich jedes Mal, wenn eine Stimme abgegeben wird.

假设zookeeper services有五台机器

Server1		Server2 	Server3 	Server4 	Server5
myid1		myid2		myid3		myid4		myid5

1) Server 1 startet und leitet eine Wahl ein. Server 1 gibt seine Stimme ab. Zu diesem Zeitpunkt verfügt der Server über 1 Stimme und eine Stimme. Wenn nicht genügend Stimmen vorhanden sind (3 Stimmen), kann die Wahl nicht abgeschlossen werden und der Status von Server 1 bleibt SUCHE.

2) Server 2 startet und leitet eine weitere Wahl ein. Server 1 und 2 stimmen jeweils für sich selbst und tauschen Abstimmungsinformationen aus**: Zu diesem Zeitpunkt stellt Server 1 fest, dass die MyID von Server 2 größer ist als die, für die er derzeit abstimmt (Server 1), und ändert die Abstimmung, um den Server zu empfehlen 2**. Zu diesem Zeitpunkt hat Server 1 0 Stimmen und Server 2 2 Stimmen. Ohne mehr als die Hälfte kann die Wahl nicht abgeschlossen werden und der Status der Server 1 und 2 bleibt LOOKING.

3) Server 3 startet und leitet eine Wahl ein. Zu diesem Zeitpunkt ändern die Server 1 und 2 die Stimmen auf Server 3. Die Ergebnisse dieser Abstimmung: Server 1 hat 0 Stimmen, Server 2 hat 0 Stimmen und Server 3 hat 3 Stimmen. Zu diesem Zeitpunkt hat Server 3 mehr als die Hälfte der Stimmen und der Server wird zum Anführer gewählt . Der Status der Server 1 und 2 wird in FOLLWERING geändert, und der Status von Server 3 wird in LEADERING geändert.

4) Server 4 startet und initiiert eine Wahl. Zu diesem Zeitpunkt haben die Server 1, 2 und 3 die Statusleiste LOOKING bereitgestellt und ändern die Abstimmungsinformationen nicht mehr. Die Ergebnisse des Austauschs von Abstimmungsinformationen: Server 3 hat 3 Stimmen und Server 4 hat 1 Stimme. Zu diesem Zeitpunkt gehorcht Server 4, ändert das Abstimmungsergebnis auf Server 3 und ändert den Status in FOLLWERING

5) Server 5 startet und fungiert als jüngerer Bruder wie Server 4.

3.2 Zookeeper-Wahlmechanismus (nicht der erste Start)

假设zookeeper services有五台机器

Server1		Server2 	Server3 	Server4 	Server5
myid1		myid2		myid3		myid4		myid5

follwer		follwer 	leadr		follwer		follwer

1) Wenn ein Server im Zookeeper-Cluster auf die folgenden zwei Situationen stößt, beginnt er mit der Eingabe der Wahl:

  • Start der Serverinitialisierung
  • Die Verbindung mit Leader kann nicht aufrechterhalten werden, während der Server läuft

2) Wenn ein Server in den Leader-Wahlprozess eintritt, kann sich der aktuelle Cluster auch in den folgenden zwei Zuständen befinden:

  • Es gibt bereits einen Leader im Cluster

In dieser Situation, in der ein Anführer vorhanden ist, wird die Maschine beim Versuch, einen Anführer zu wählen, über die Anführerinformationen des aktuellen Servers informiert. Für die Maschine muss lediglich eine Verbindung mit der Anführermaschine hergestellt und der Status synchronisiert werden.

  • Es gibt tatsächlich keinen Anführer im Cluster
假设zookeeper services有五台服务器组成
SID分别为1、2、3、4、5
ZXID分别为8、8、8、7、7  并且此时SID为3的服务器是Leader
某一时刻,服务器3和5出现故障,因此需要重新进行Leader选举:
							(EPOCH,ZXID,SID)	(EPOCH,ZXID,SID)	(EPOCH,ZXID,SID)
SID为1、2、4的机器投票情况	 (1,8,7)			 (1,8,2)			 (1,7,4)

		选举Leader规则:
				1)EPOCH大的直接胜出
				2)EPOCH相同,事务ID(ZXID)大的胜出
				3)事务ID相同,服务器ID(SID)大的胜出
				
# 最终结果
服务器4当选Leader,服务器1、2为Follwer

4 Der Client schreibt den Datenprozess auf den Server

Die Schreibanfrage des Schreibprozesses wird direkt an den Leiter gesendet


					1 write								 2 write
Client ------------------------------>  ZK Server1 Leader ---------->ZK Server2 Follwer 
					3 ack 
ZK Server2 Follwer ---------->ZK Server1 Leader  

(因此该集群中只有三台机器,数据写入了两台,超过半数了,zk Server Leader回应Client)
 					4 ack
ZK Server1 Leader  ------> Client 
 				   5 write
ZK Server1 Leader ------> ZK Server3 Follwer
 				   6 ack
ZK Server3 Follwer ------> ZK Server1 Leader

Die Schreibanfrage des Schreibprozesses wird an Follower gesendet

			1 write								2 write请求
Client -------------------> ZK Server2 Follwer ----------> ZK Server1 Leader
					3 write
ZK Server1 Leader  ----------> ZK Server2 Follwer 
					4 ack
ZK Server2 Follwer ----------> ZK Server1 Leader

(因此该集群中只有三台机器,数据写入了两台,超过半数了,zk Server Leader回应ZK Server2 Follwer )
				  5 ack
ZK Server1 Leader ----------> ZK Server2 Follwer 
(接着ZK Server2 Follwer 回应Client)
					6 ack
ZK Server2 Follwer ----------------> Client
					7 write 
ZK Server1 Leader ---------> ZK Server3 Follwer
					8 ack
ZK Server3 Follwer ----------> ZK Server1 Leader

5 Zookeeper-Befehlsoperationen

5.1 Zookeeper-Datenmodell

Zookeeper ist ein Baumverzeichnisdienst, dessen Datenmodell dem Verzeichnisbaum des Unix-Dateisystems sehr ähnlich ist und eine hierarchische Struktur aufweist.

Jeder Knoten wird hier als ZNode bezeichnet und jeder Knoten speichert seine eigenen Daten und Knoteninformationen. Zu den Knoteninformationen gehören: Datenlänge, Erstellungszeit, Änderungszeit, Anzahl der untergeordneten Knoten usw.

Knoten können untergeordnete Knoten haben und eine kleine Datenmenge (1 MB) darf unter dem Knoten gespeichert werden.

5.2 ZK-Knotentyp

Knoten können in vier Hauptkategorien unterteilt werden:

  • ​ PERSISTENT persistenter Knoten, permanenter Knoten
  • EPHEMERAL temporärer Knoten -e
  • ​ PERSISTENT_SEQUENTIAL Persistenzsequenzknoten: -s
  • ​ EPHEMERAL_SEQUENTIAL temporärer Sequenzknoten: -es

Persistenzknoten:

​Knoten werden nicht automatisch gelöscht, nachdem der Zookeeper-Client beendet wird . Der Zookeeper-Client erstellt standardmäßig persistente Knoten .

Temporärer Knoten:

​ Nach dem Beenden des Zookeeper- Clients wird der Knoten automatisch gelöscht . Temporäre Knoten können keine untergeordneten Knoten haben. Benutzer können das Öffnen oder Schließen verteilter Dienste über temporäre Knoten beurteilen.

Sequenzknoten:

Am Ende des Knotennamens wird automatisch eine 10-stellige Seriennummer des Knotens angehängt.

Persistenzsequenzknoten:

Nachdem der Client die Verbindung zu zk getrennt hat, existiert der Knoten noch, aber zk nummeriert den Knoten fortlaufend.

Temporärer Sequenzknoten:

Nachdem der Client die Verbindung zu ZooKeeper getrennt hat, wird der Knoten automatisch gelöscht, ZooKeeper nummeriert den Knoten jedoch fortlaufend.

Demo

[root@kk01 ~]# zkCli.sh -server 192.168.188.128:2181
# 1、创建 永久节点(不带序号) 默认
[zk: 192.168.188.128:2181(CONNECTED) 0] create /nhk "ninghongkang"   
Created /nhk
[zk: 192.168.188.128:2181(CONNECTED) 6] ls /    # 查看
[nhk, zookeeper]
[zk: 192.168.188.128:2181(CONNECTED) 7] get /nhk    # 查看节点值
ninghongkang

# 2.创建带序号永久节点 (带序号的节点好处在与可以重复创建,他会在后面默认拼接10位的序列号)
[zk: 192.168.188.128:2181(CONNECTED) 9] create -s /nhk/app1 "test1"
Created /nhk/app10000000000
[zk: 192.168.188.128:2181(CONNECTED) 17] ls /nhk
[app10000000000]
[zk: 192.168.188.128:2181(CONNECTED) 18] get /nhk/app10000000000
test1
[zk: 192.168.188.128:2181(CONNECTED) 19] create -s /nhk/app1 "test1"
Created /nhk/app10000000001
[zk: 192.168.188.128:2181(CONNECTED) 20] ls /nhk
[app10000000000, app10000000001]

# 3.创建临时节点
[zk: 192.168.188.128:2181(CONNECTED) 21] create -e /nhk/app2    # 创建临时节点
Created /nhk/app2
[zk: 192.168.188.128:2181(CONNECTED) 22] create -e /nhk/app2    # 重复创建显示节点已存在
Node already exists: /nhk/app2
# 4.创建临时顺序节点(可重复创建)
[zk: 192.168.188.128:2181(CONNECTED) 23] create -e -s /nhk/app2
Created /nhk/app20000000003
[zk: 192.168.188.128:2181(CONNECTED) 24] create -e -s /nhk/app2
Created /nhk/app20000000004
[zk: 192.168.188.128:2181(CONNECTED) 27] ls /nhk
[app10000000000, app10000000001, app2, app20000000003, app20000000004]

# 重启zk客户端
[zk: 192.168.188.128:2181(CONNECTED) 28] quit

[root@kk01 ~]# zkCli.sh -server 192.168.188.128:2181
[zk: 192.168.188.128:2181(CONNECTED) 1] ls /nhk   # 查看发现临时节点已经被自动删除了
[app10000000000, app10000000001]

Der Zookeeper-Server kann mit dem Zookeeper-Client und der Zookeeper-Java-API interagieren

5.3 Allgemeine Befehle auf dem ZooKeeper-Server

Aufschlag Befehl
Starten Sie den ZK-Dienst ./zkServer.sh starten
Überprüfen Sie den Status des ZK-Dienstes ./zkServer.sh starten
Stoppen Sie den ZK-Dienst ./zkServer.sh stoppen
Starten Sie den ZK-Dienst neu ./zkServer.sh neu starten

5.4 Allgemeine Befehle des Zookeeper-Clients

Berechtigungskontrolle für Zookeeper-Knoten

In der tatsächlichen Produktion verwenden mehrere Anwendungen häufig denselben Zookeeper, aber unterschiedliche Anwendungssysteme verwenden selten gemeinsame Daten.

Angesichts dieser Situation verwendet zookeeper die ACL-Richtlinie (Access Control List) zur Berechtigungssteuerung, die der Berechtigungssteuerung des Linux-Dateisystems ähnelt. Zookeeper-Knoten definieren 5 Berechtigungen.

  • ​ Berechtigung zum Erstellen untergeordneter Knoten erstellen
  • ​ Leseberechtigung zum Abrufen der Daten des untergeordneten Knotens und der Liste der untergeordneten Knoten
  • ​ Schreibberechtigung zum Aktualisieren von Knotendaten
  • ​delete-Berechtigung zum Löschen untergeordneter Knoten
  • ​ andin legt die Berechtigungen des Knotens fest
Aufschlag Befehl
Stellen Sie eine Verbindung zum lokalen zk-Client her zkCli.sh (mit lokalem Client verbinden)
Stellen Sie eine Verbindung zum Zookeeper-Server her ./zkCli.sh -server [ip:port] Wenn Sie eine Verbindung zu einem Remote-Server herstellen, müssen Sie die IP und den Port angeben.
Trennen aufhören
Befehlshilfe anzeigen helfen
Knoten im angegebenen Verzeichnis anzeigen ls /pfad
Achten Sie auf Änderungen in untergeordneten Knoten ls -w /pfad
Zusätzliche Sekundärinformationen ls -s /pfad
Knoten erstellen Erstellungs-/Knotenpfad [Wert]
Erstellen Sie Sequenzknoten create -s /node path [Wert]
Erstellen Sie einen temporären Knoten create -e /node path [Wert] (der zk-Client wird automatisch gelöscht, wenn er neu startet oder eine Zeitüberschreitung auftritt)
Knotenwert abrufen /node-Pfad abrufen
Überwachen Sie Änderungen im Knoteninhalt (d. h. im Knoten gespeicherte Daten). get -w /path
Zusätzliche Sekundärinformationen get -s /path
Knotenwert festlegen Legen Sie den /node-Pfadwert fest
Löschen Sie einen einzelnen Knoten /node-Pfad löschen
Knoten mit untergeordneten Knoten löschen (rekursives Löschen) deleteall/Knotenpfad
Knotenstatus anzeigen Statistik/Pfad
Knotendetails anzeigen ls2 /Knotenpfad (nicht empfohlen) ls -s /Knotenpfad

Hinweis: Befehle zum Betreiben von Knoten im Zookeeper-Client müssen absolute Pfade verwenden.

Einige Parameterdetails:

  • czxid: Transaktions-ID des erstellten Knotens
  • ctime: Erstellungszeit (Anzahl der Millisekunden seit der Erstellung von znode, seit 1970)
  • mzxid: Die letzte aktualisierte Transaktions-ID (zxid der letzten aktualisierten Transaktion von znode)
  • mtime: Änderungszeit (Anzahl der Millisekunden seit der letzten Änderung von znode, seit 1970)
  • pzxid: Die Transaktions-ID der zuletzt aktualisierten untergeordneten Knotenliste (zxid des zuletzt aktualisierten untergeordneten Knotens von znode)
  • cversion: Versionsnummer des untergeordneten Knotens (Änderungsnummer des untergeordneten Znode-Knotens, Änderungsnummer des untergeordneten Zonde-Knotens)
  • Datenversion: Datenversionsnummer (Zone-Datenänderungsnummer)
  • aclversion: Berechtigungsversionsnummer (Nummer der Zonde-Zugriffskontrollliste ändern)
  • epheralOwner: Wird für temporäre Knoten verwendet und stellt die Transaktions-ID des temporären Knotens dar. 0, wenn es sich um einen dauerhaften Knoten handelt (wenn es sich um einen temporären Knoten handelt, die Sitzungs-ID des Eigentümers dieser Zone. Wenn es sich nicht um einen temporären Knoten handelt, ist er es ist 0)
  • Datenlänge: Die Länge der im Knoten gespeicherten Daten (Datenlänge von Znode)
  • numChildren: die Anzahl der untergeordneten Knoten des aktuellen Knotens (Anzahl der untergeordneten znode-Knoten)

5.5 Zuhörerprinzip

Detaillierte Erläuterung des Überwachungsprinzips

1) Zuerst muss es einen main()-Thread geben

2) Erstellen Sie einen Zookeeper-Client im Hauptthread. Zu diesem Zeitpunkt werden zwei Threads erstellt, einer für die Netzwerkverbindung (Connect) und einer für das Abhören (Listener).

3) Senden Sie registrierte Abhörereignisse über den Verbindungsthread an zookeeper

4) Fügen Sie die registrierten Listening-Ereignisse zur Listener-Liste des Zookeepers hinzu.

5) Wenn zookeeper Datenänderungen oder Pfadänderungen erkennt, sendet er diese Nachricht an den Listener-Thread.

6) Die Methode „process()“ wird intern im Listener-Thread aufgerufen

		zk客户端											zk服务端
	1 Main()线程
	2 创建zkClient				5"/"路径数据发生变化      注册的监听器列表
			|-- Listener     <---------------------  	  4 Client:ip:port:/path
								3 getChildren("/",true)
			|--connect       --------------------->     
            
	6 listener线程调用process()

Szenenüberwachung

1)监听节点数据的变化
get path [watch]

2)监听子节点增减的变化
ls path [watch] 

Demo

# 1.节点的值变化监听
# 1)在kk01上注册监听/nhk节点 数据变化
[zk: localhost:2181(CONNECTED) 0] get -w /nhk
ninghongkang

# 2)在kk02主机上修改/nhk节点的数据
[zk: localhost:2181(CONNECTED) 2] set /nhk nhk666
[zk: localhost:2181(CONNECTED) 3] 

# 3)观察kk01主机收到的数据变化的监听
WATCHER::

WatchedEvent state:SyncConnected type:NodeDataChanged path:/nhk

# 注意:在kk02上多次修改/nhk节点的值,kk01不会再继续监听。因为注册一次,只能监听一次。如果想再次监听,需要再次注册


# 2.节点的子节点变化监听(路径变化)
# 1)在kk01上注册监听/nhk节点 子节点变化
[zk: localhost:2181(CONNECTED) 2] ls -w /nhk
[app10000000000, app10000000001]

# 2)在kk02主机上/nhk节点上创建新节点app2
[zk: localhost:2181(CONNECTED) 4] create /nhk/app2 "test2"
Created /nhk/app2

# 3)观察kk01主机收到的子节点变化的监听
WATCHER::

WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/nhk

# 注意:节点的路径变化,也是注册一次,生效一次。想多次生效。就需要多次注册

Beachten:

  • Die Überwachung von Knotendatenänderungen und die einmalige Registrierung können nur einmal überwacht werden. Wenn Sie noch einmal zuhören möchten, müssen Sie sich erneut registrieren

  • Auch die Pfadänderung des Knotens wird einmalig registriert und ist einmalig wirksam. Möchte mehrfach wirksam werden. Sie müssen sich mehrmals registrieren

6 ZooKeeper Java API-Operationen

6.1 Native Zookeeper-API

1) Vorbereitung der Schöpfung

Stellen Sie sicher, dass der zookeeper-Clusterserver auf den Servern kk01, kk02 und kk03 gestartet ist.

Fügen Sie verwandte Abhängigkeiten in pom hinzu

<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.6.1</version>
        </dependency>
    </dependencies>

Erstellen Sie eine neue log4j.properties-Datei im Verzeichnis src/main/sources mit dem folgenden Inhalt

#############
# 输出到控制台
#############
# log4j.rootLogger日志输出类别和级别:只输出不低于该级别的日志信息DEBUG < INFO < WARN < ERROR < FATAL
# INFO:日志级别     CONSOLE:输出位置自己定义的一个名字     
log4j.rootLogger=INFO,CONSOLE
# 配置CONSOLE输出到控制台
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
# 配置CONSOLE设置为自定义布局模式
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
# 配置CONSOLE日志的输出格式  [frame] 2019-08-22 22:52:12,000  %r耗费毫秒数 %p日志的优先级 %t线程名 %C所属类名通常为全类名 %L代码中的行号 %x线程相关联的NDC %m日志 %n换行
log4j.appender.CONSOLE.layout.ConversionPattern=[frame] %d{yyyy-MM-dd HH:mm:ss,SSS} - %-4r %-5p [%t] %C:%L %x - %m%n

2) Erstellen Sie einen Zookeeper-Client

public class ZKClient {
    
    
    //  注意:逗号左右不能有空格,否则会连接不成功
    private static String connectString = "kk01:2181,kk02:2181,kk03:2181";
    private int sessionTimeout = 2000;
    private ZooKeeper zkClient = null;

    @Before
    public void init() throws IOException, InterruptedException {
    
    
        zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
    
    
            @Override
            public void process(WatchedEvent event) {
    
    

            }
        });
    }

    @After
    public void close(){
    
    
        try {
    
    
            zkClient.close();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
}

3) Untergeordnete Knoten erstellen

    @Test
    public void create() {
    
    
        try {
    
    
            //   final String path, 要创建的节点的路径
            //   byte[] data, 节点数据
            //   List<ACL> acl,  节点权限
            //   CreateMode createMode  节点的类型
            zkClient.create("/test", "666".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        } catch (KeeperException e) {
    
    
            e.printStackTrace();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }

Test: Überprüfen Sie die Erstellung von Knoten im zk-Client von kk01

[zk: localhost:2181(CONNECTED) 3] ls /
[nhk, test, zookeeper]

4) Untergeordnete Knoten abrufen und auf Änderungen in untergeordneten Knoten achten

@Before
    public void init() throws IOException, InterruptedException {
    
    
        zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
    
    
            @Override
            public void process(WatchedEvent event) {
    
    
                List<String> children = null;
                try {
    
    
                    children = zkClient.getChildren("/test", true);
                } catch (KeeperException e) {
    
    
                    e.printStackTrace();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                System.out.println("------------------");
                for (String child : children) {
    
    
                    System.out.println(child);
                }
            }
        });
    }

@Test
    public void getChildren() throws InterruptedException {
    
    
        List<String> children = null;
        try {
    
    
            children = zkClient.getChildren("/test", true);
        } catch (KeeperException e) {
    
    
            e.printStackTrace();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("------------------");
        for (String child:children) {
    
    
            System.out.println(child);
        }

        // 为了让程序不那么快结束,我们让程序睡起来
        Thread.sleep(Integer.MAX_VALUE);
    }

Test: Erstellen Sie die Unterknoten app1 und app2 unter dem zk-Client von kk01, um Änderungen/Testknoten anzuzeigen

[zk: localhost:2181(CONNECTED) 7] create /test/app1 "t1"
Created /test/app1
[zk: localhost:2181(CONNECTED) 8] create /test/app2 "t2"
Created /test/app2

Die Ideenkonsole gibt die folgenden Informationen aus

------------------
------------------
------------------
app1
------------------
app2
app1

5) Bestimmen Sie, ob ZNode vorhanden ist

   @Test
    public void exist(){
    
    
        Stat status = null;
        try {
    
    
            status = zkClient.exists("/test", false);
        } catch (KeeperException e) {
    
    
            e.printStackTrace();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println(status == null? "节点不存在":"节点存在");
        
    }

6.2 Kurator-API

Zookeeper stellt eine Java-API bereit, um Entwicklern die Durchführung von Client-Programmierungen und den bedarfsgerechten Betrieb von Daten auf dem Server zu erleichtern.

Entwicklungsumgebung konfigurieren

Ändern Sie den Inhalt der pom.xml-Datei in IDEA wie folgt:

<!-- 导入以下依赖坐标 zk依赖记得要与zk版本一致-->
<dependency>
	<groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.6.1</version> 
</dependency>
 <!-- 单元测试 -->
<dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
	<version>4.10</version>
	<scope>test</scope>
 </dependency>

6.2.1 Einführung in Curator

Curator ist eine Java-Clientbibliothek für Apache Zookeeper

Gemeinsame Zookeeper-Java-API:

  • ​ Native Java-API
  • ​ ZKClient
  • Kurator

Ziel des Curator-Projekts ist es, die Nutzung von Zookeeper-Clients zu vereinfachen

Curator wurde ursprünglich von Netflix entwickelt und später an die Apache Foundation gespendet. Derzeit ist es ein Spitzenprojekt von Apache.

6.2.2 Allgemeine Operationen der Curator-API

Vorbereitung

Importieren Sie verwandte Abhängigkeiten in POM

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

        <!-- 单元测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
            <scope>test</scope>
        </dependency>

        <!--log4j相关jar-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.21</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.21</version>
        </dependency>
        
   <!-- 中文乱码问题 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.3</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>utf-8</encoding>
                </configuration>
            </plugin>

log.properties-Datei

log4j.rootLogger=DEBUG,console
#----------------输出为控制台-------------------#
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.ImmediateFlush=true
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%p][%d{yyyy-MM-dd HH:mm:ss}][%c]%m%n

Verbindung herstellen

Es wird empfohlen, die zweite Verbindungsmethode zu verwenden

public class CuratorTest {
    
    
    private static CuratorFramework client;

    /**
     * 建立连接
     */
    @Before
    public void testConnect() {
    
    
        /**
         * String connectString,  连接字符串
         * int sessionTimeoutMs,  会话超时时间 单位ms
         * int connectionTimeoutMs,  连接超时时间 单位ms
         * RetryPolicy retryPolicy  重试策略
         */
        // 重试策略
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000, 10);

        // 第一种连接方式
        //CuratorFramework curator = CuratorFrameworkFactory.newClient("192.168.188.128:2181", 60 * 1000, 15 * 1000, retryPolicy);
        // 开启连接
        //curator.start();

        // 第二种连接
        client = CuratorFrameworkFactory.builder().connectString("192.168.188.128:2181").
                sessionTimeoutMs(60 * 1000).
                connectionTimeoutMs(15 * 1000).
                retryPolicy(retryPolicy).namespace("clear").build(); // 在根节点指定命名空间
        		// 注:使用该方式创建的命名空间,当其下无节点时,断开链接会自动将命名空间删除

        // 开启连接
        client.start();
    }

    /**
     * 关闭连接
     */
    @After
    public void close() {
    
    
        if (client != null) {
    
    
            client.close();
        }
    }
}

Knoten hinzufügen

Grundlegende Erstellung

 /**
     * 创建节点:create 持久 临时 顺序 数据
     * 1.基本创建
     * 2.创建节点 带有数据
     * 3.设置节点的类型
     * 4.创建多级节点 /app1/p1
     */
    @Test
    public void testCreate(){
    
    
        // 1.基本创建
        // 如果创建节点,没有指明数据,则默认将当前客户端的ip作为数据存储
        try {
    
    
            String path = client.create().forPath("/app1");
            System.out.println(path);
        } catch (Exception e) {
    
    
            System.out.println("创建失败~~~");
            e.printStackTrace();
        }
 }
    

prüfen

Nachdem Sie den Test in idea ausgeführt haben, werden in zkclient die folgenden Informationen angezeigt

[zk: localhost:2181(CONNECTED) 11] get /clear/app1
192.168.56.1

Erstellen Sie einen Knoten mit Daten

 /**
     * 创建节点:create 持久 临时 顺序 数据
     * 1.基本创建
     * 2.创建节点 带有数据
     * 3.设置节点的类型
     * 4.创建多级节点 /app1/p1
     */
    @Test
    public void testCreate2(){
    
    
        // 2.创建节点 带有数据
        try {
    
    
            // 如果创建节点,没有指明数据,则默认将当前客户端的ip作为数据存储
            String path = client.create().forPath("/app2","zjh".getBytes());
            System.out.println(path);
        } catch (Exception e) {
    
    
            System.out.println("创建失败~~~");
            e.printStackTrace();
        }
    }

prüfen

Nachdem Sie den Test in idea ausgeführt haben, werden in zkclient die folgenden Informationen angezeigt

[zk: localhost:2181(CONNECTED) 12] get /clear/app2
zjh

Knoten erstellen und Typ festlegen

  /**
     * 创建节点:create 持久 临时 顺序 数据
     * 1.基本创建
     * 2.创建节点 带有数据
     * 3.设置节点的类型
     * 4.创建多级节点 /app1/p1
     */
    @Test
    public void testCreate3(){
        // 3.设置节点的类型
        // 默认类型:持久化
        try {
         // 创建临时节点
            String path = client.create().withMode(CreateMode.EPHEMERAL).forPath("/app3","zjh".getBytes());
            System.out.println(path);
            
        } catch (Exception e) {
            System.out.println("创建失败~~~");
            e.printStackTrace();
        }
        
        while (true){
            // 因为是测试,所以弄个死循环保持客户端不断开连接
        }
}

prüfen

Nachdem Sie den Test in idea ausgeführt haben, werden in zkclient die folgenden Informationen angezeigt

[zk: localhost:2181(CONNECTED) 29] ls /clear
[app1, app2, app3]

Dann unterbrechen wir die Idee und sehen die folgenden Informationen in zkclient: App3 verschwindet

[zk: localhost:2181(CONNECTED) 29] ls /clear
[app1, app2, app3]
[zk: localhost:2181(CONNECTED) 30] ls /clear
[app1, app2]

Erstellen Sie mehrstufige Knoten

/**
     * 创建节点:create 持久 临时 顺序 数据
     * 1.基本创建
     * 2.创建节点 带有数据
     * 3.设置节点的类型
     * 4.创建多级节点 /app1/p1
     */
    @Test
    public void testCreate4(){
    
    
        // 4.创建多级节点 /app4/p1
        try {
    
    
            // 创建临时节点
            String path = client.create().creatingParentContainersIfNeeded().forPath("/app4/p1","zjh".getBytes());
            System.out.println(path);
        } catch (Exception e) {
    
    
            System.out.println("创建失败~~~");
            e.printStackTrace();
        }
        while (true){
    
    
            // 因为是测试,所以弄个死循环保持客户端不断开连接
        }
    }

prüfen

Nachdem Sie den Test in der Idee ausgeführt haben, werden in zkclient die folgenden Informationen angezeigt, die darauf hinweisen, dass das mehrstufige Verzeichnis erfolgreich erstellt wurde.

[zk: localhost:2181(CONNECTED) 17] ls /clear/app4
[p1]

Zusammenfassen

  • Knoten erstellen: create().forPath(" ")
  • Erstellen Sie einen Knoten mit Daten: create().forPath("",data)
  • Legen Sie den Knotentyp fest: create().withMode().forPath("",data)
  • Erstellen Sie mehrstufige Knoten: create().creatingParentContainersIfNeeded().forPath("",data)

Knoten löschen

Löschen Sie einen einzelnen Knoten

 /**
     * 删除节点
     */
    @Test
    public void testDelete(){
        try {
             // 删除单个节点
            client.delete().forPath("/app1");
        } catch (Exception e) {
            System.out.println("删除失败~~~");
            e.printStackTrace();
        }
    }

prüfen

Wenn der entsprechende Knoten app1 nach dem Ausführen des Tests in der Idee nicht in zkclient abgefragt werden kann, bedeutet dies, dass der Löschvorgang erfolgreich war.

[zk: localhost:2181(CONNECTED) 5] ls /clear
[app2, app4]

Knoten mit untergeordneten Knoten löschen

/**
     * 删除节点
     */
    @Test
    public void testDelete2(){
    
    
        // 删除带子节点的节点
        try {
    
    
            client.delete().deletingChildrenIfNeeded().forPath("/app4");
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }

prüfen

Wenn der entsprechende Knoten app4 nach dem Ausführen des Tests in der Idee nicht in zkclient abgefragt werden kann, bedeutet dies, dass der Löschvorgang erfolgreich war.

[zk: localhost:2181(CONNECTED) 14] ls /clear
[app2]

Muss erfolgreich gelöscht werden

Um Netzwerk-Jitter zu verhindern. Die Essenz besteht darin, es erneut zu versuchen, wenn der Löschvorgang nicht erfolgreich ist.

    /**
     * 删除节点
     */
    @Test
    public void testDelete3(){
        // 必须成功删除节点
        try {
            client.delete().guaranteed().forPath("/app2");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

prüfen

Wenn der entsprechende Knoten app2 nach dem Ausführen des Tests in der Idee nicht in zkclient abgefragt werden kann, bedeutet dies, dass der Löschvorgang erfolgreich war.

[zk: localhost:2181(CONNECTED) 17] ls /clear
[]

Ruf zurück

/**
     * 删除节点
     */
    @Test
    public void testDelete4(){
    
    
        try{
    
    
            // 回调
            client.delete().guaranteed().inBackground(new BackgroundCallback() {
    
    
                @Override
                public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
    
    
                    System.out.println("我被删除了~");
                    System.out.println(event);
                }
            }).forPath("/app1");
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }

prüfen

Nachdem der Test in der Idee ausgeführt wurde, werden die relevanten Informationen auf der Konsole gedruckt. Wenn der entsprechende Knoten app1 in zkclient nicht abgefragt werden kann, bedeutet dies, dass der Löschvorgang erfolgreich war. Wenn im Namespace kein Knoten vorhanden ist, wird auch der Namespace gelöscht.

[zk: localhost:2181(CONNECTED) 28] ls /
[hbase, zookeeper]

Zusammenfassen

  • Knoten löschen: delete deleteall
  • 1. Einen einzelnen Knoten löschen: delete().forPath("")
  • 2. Knoten mit untergeordneten Knoten löschen: delete().deletingChildrenIfNeeded().forPath("")
  • 3. Der Knoten muss erfolgreich gelöscht werden: um Netzwerkjitter zu verhindern. Die Essenz besteht darin, es erneut zu versuchen: delete().guaranteed().forPath("");
  • 4.Rückruf: imHintergrund

Knoten ändern

die Daten ändern

   /**
     * 修改数据
     * 1.修改数据
     * 2.根据版本修改
     */
    @Test
    public void testSet(){
    
    
        try {
    
    
            // 修改数据
            client.setData().forPath("/app1","miss you".getBytes());
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }

prüfen

Nachdem Sie den Test in der Idee ausgeführt haben, werden in zkclient die folgenden Informationen angezeigt, die darauf hinweisen, dass die Änderung erfolgreich war.

[zk: localhost:2181(CONNECTED) 25] get /clear/app1
miss you

Je nach Version ändern

    /**
     * 修改数据
     * 1.修改数据
     * 2.根据版本修改
     */
    @Test
    public void testSeForVersion() throws Exception {
        Stat status = new Stat();
        // 查询节点状态信息
        client.getData().storingStatIn(status).forPath("/app1");

        int version = status.getVersion(); // 查询出来的结果
        System.out.println(version);
        // 根据版本修改
        try {
            client.setData().withVersion(version).forPath("/app1","dont".getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

prüfen

Nachdem Sie den Test in der Idee ausgeführt haben, werden in zkclient die folgenden Informationen angezeigt, die darauf hinweisen, dass die Änderung erfolgreich war.

[zk: localhost:2181(CONNECTED) 29] get /clear/app1
dont

Zusammenfassen

die Daten ändern

  • Datensatz ändern setData().forPath("", data);

  • Ändern Sie setData().withVersion(version information).forPath("", data) entsprechend der Version;

Abfrageknoten

Knotendaten abfragen

   /**
     * 查询节点:
     * 1.查询数据:get
     * 2.查询子节点:ls
     * 3.查询节点状态信息:ls -s
     */
    @Test
    public void testGet1() {
    
    
        try {
    
    
            // 查询节点数据
            byte[] data = client.getData().forPath("/app1");  // 路径前必须加/ 否则报错 Path must start with / character
            System.out.println(new String(data));
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }

Untergeordnete Knoten abfragen

 /**
     * 查询节点:
     * 1.查询数据:get
     * 2.查询子节点:ls
     * 3.查询节点状态信息:ls -s
     */
    @Test
    public void testGet2() {
    
    
        List<String> path = null;
        try {
    
    
            // 查询子节点 ls
            path = client.getChildren().forPath("/app4");
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        for (String p :path) {
    
    
            System.out.println(p);
        }
    }

Knotenstatusinformationen abfragen

  /**
     * 查询节点:
     * 1.查询数据:get
     * 2.查询子节点:ls
     * 3.查询节点状态信息:ls -s
     */
    @Test
    public void testGet3() {
    
    
        Stat status = new Stat();
        /**
         * 如下是各种状态信息,需要时直接调用 get set方法即可
         * public class Stat implements Record {
         *     private long czxid;
         *     private long mzxid;
         *     private long ctime;
         *     private long mtime;
         *     private int version;
         *     private int cversion;
         *     private int aversion;
         *     private long ephemeralOwner;
         *     private int dataLength;
         *     private int numChildren;
         *     private long pzxid;
         */
        System.out.println(status);
        // 查询节点状态信息:ls -s
        try {
    
    
            client.getData().storingStatIn(status).forPath("/app1");
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        System.out.println(status);  // 仅用于测试,真实项目中不会是这个用发
    }

Zusammenfassen

  • 1. Daten abfragen: getgetData().forPath("")
  • 2. Untergeordnete Knoten abfragen: ls getChildren().forPath("")
  • 3. Knotenstatusinformationen abfragen: ls -s getData().storingStatIn(status object).forPath("");

7 Beobachten Sie die Ereignisüberwachung

  • Mit Zookeeper können Benutzer einige Beobachter auf bestimmten Knoten registrieren. Wenn bestimmte Ereignisse ausgelöst werden, benachrichtigt der Zookeeper-Server interessierte Clients über das Ereignis. Dieser Mechanismus ist ein wichtiges Merkmal des verteilten Koordinationsdienstes für die Schulung von Zookeeper.

  • Der Watcher-Mechanismus wird in Zookeeper eingeführt, um die Publish/Subscribe-Funktion zu implementieren, die es mehreren Abonnenten ermöglicht, ein Objekt gleichzeitig zu überwachen. Wenn sich der Status eines Objekts selbst ändert, werden alle Abonnenten benachrichtigt.

  • Zookeeper unterstützt die Ereignisüberwachung nativ durch die Registrierung von Watchern. Die Verwendung ist jedoch nicht besonders praktisch und erfordert, dass Entwickler Watcher wiederholt registrieren, was umständlich ist.

  • Curator führt Cache ein, um serverseitige Ereignisse von Zookeeper zu überwachen

  • Zookeeper bietet drei Arten von Beobachtern:

1) NodeCache: Überwachen Sie einfach einen bestimmten Knoten

2) PathChildrenCache: Überwachen Sie die untergeordneten Knoten eines ZNode

3) TreeCache: Kann alle Knoten im gesamten Baum überwachen, ähnlich der Kombination aus PathChildrenCache und PathChildrenCache

7.1 NodeCache-Demo

Die Vorgänge zum Herstellen und Schließen von Verbindungen sind die gleichen wie oben und werden hier nicht wiederholt.

/**
 * 演示 NodeCache:给指定的节点注册监听器
 */
@Test
public void testNodeCache() {
    
    
    // 1.创建NodeCache对象
    NodeCache nodeCache = new NodeCache(client, "/app1");
    // 2.注册监听
    /*    nodeCache.getListenable().addListener(new NodeCacheListener() {
            @Override
            public void nodeChanged() throws Exception {
                System.out.println("节点变化了~");
            }
        });*/
    nodeCache.getListenable().addListener(() -> System.out.print("节点变化了~"));
    // 3.开启监听 如果设置为true,则开启监听,加载缓冲数据
    try {
    
    
        nodeCache.start(true);
    } catch (Exception e) {
    
    
        e.printStackTrace();
    }

    while (true) {
    
    
        // 因为是测试,所以弄个死循环保持客户端不断开连接
    }
}

prüfen

Wenn wir auf dem Zookeeper-Client Änderungen am /clear/app1-Knoten vornehmen (Knoten hinzufügen, löschen, ändern), wird die IEDA-Konsole die Knotenänderungen ausdrucken~

7.2 PathChildrenCache-Demo

/**
 * 演示 PathChildrenCache 监听某个节点的所有孩子节点们
 */
@Test
public void testPathChildrenCache() throws Exception {
    
    
    // 1.创建监听对象
    PathChildrenCache pathChildrenCache = new PathChildrenCache(client, "/app2",true);
    // 2.判断监听器
    pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
    
    
        @Override  // 可用lambda表达式简化
        public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
    
    
            System.out.println("子节点变化了~");
            System.out.println(event);
            // 监听子节点的数据变更,并且拿到变更后的数据
            // 1)获取类型
            PathChildrenCacheEvent.Type type = event.getType();
            // 2)判断类型是否是update
            if(type.equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)){
    
    
                System.out.println("子节点的数据变更了~");
                // 第一个getData()应该是获取子节点
                // 第二格getData()应该是获取子节点的数据
                byte[] data = event.getData().getData();
                System.out.println(new String(data));  // 字节数组以ASCII码形式输出

            }
        }
    });
    // 3.开启监听
    pathChildrenCache.start();
    while (true) {
    
    
        // 因为是测试,所以弄个死循环保持客户端不断开连接
    }
}

7.3 TreeCache-Demonstration

 /**
     * 演示 TreeCache 监听某个节点自己和其子节点们
     */
    @Test
    public void testTreeCache() {
    
    
        // 1.创建监听器
        TreeCache treeCache = new TreeCache(client, "/app2");
        // 2.注册监听
        treeCache.getListenable().addListener(new TreeCacheListener() {
    
    
            @Override
            public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {
    
    
                System.out.println("节点变化了~~");
                System.out.println(event);
            }
        });
        // 3.开启监听
        try {
    
    
            treeCache.start();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        while (true) {
    
    
            // 因为是测试,所以弄个死循环保持客户端不断开连接
        }
    }

8 Verteilte Sperre

  • Wenn wir eigenständige Anwendungen entwickeln und eine Synchronisierung beinhalten, verwenden wir synchronisierte oder Lock-Methoden, um das Problem der Codesynchronisation zwischen Multithreads zu lösen. Zu diesem Zeitpunkt laufen Multithreads problemlos unter derselben JVM.
  • Wenn unsere Anwendung jedoch in einem verteilten Cluster arbeitet, bei dem es sich um eine Arbeitsumgebung unter mehreren JVMs handelt, können Synchronisierungsprobleme nicht durch Multithread-Sperren über JVMs hinweg gelöst werden.
  • Daher ist ein fortschrittlicherer Sperrmechanismus erforderlich, um das Problem der Datensynchronisierung zwischen Prozessen auf mehreren Maschinen zu bewältigen – also eine verteilte Sperre

8.1 Prinzip der verteilten Sperre von Zookeeper

Kernidee : Wenn der Client eine Sperre erwerben möchte, erstellt er einen Knoten und löscht den Knoten nach Verwendung der Sperre.

Wenn wir davon ausgehen, dass sich unter dem Wurzelknoten ein /lock-Knoten befindet /

1) Wenn der Client die Sperre erhält, wird unter dem Sperrknoten ein temporärer Sequenzknoten erstellt .

2) Erhalten Sie dann alle untergeordneten Knoten unter der Sperre. Nachdem der Client alle untergeordneten Knoten erhalten hat und feststellt, dass der von ihm erstellte untergeordnete Knoten die kleinste Sequenznummer hat, wird davon ausgegangen, dass der Client die Sperre erhalten hat. (Das heißt, es ist eine geringe Priorität erforderlich.) Nach Verwendung der Sperre wird der Knoten gelöscht.

3) Wenn Sie feststellen, dass der von Ihnen erstellte Knoten nicht der kleinste unter allen untergeordneten Knoten der Sperre ist, bedeutet dies, dass Sie die Sperre nicht erhalten haben. Zu diesem Zeitpunkt muss der Client den Knoten finden, der kleiner als er selbst ist Registrieren Sie einen Ereignis-Listener, damit er auf Löschereignisse wartet.

4) Wenn festgestellt wird, dass der Knoten, der kleiner als er selbst ist, gelöscht wurde, erhält der Watcher des Clients eine entsprechende Benachrichtigung. Zu diesem Zeitpunkt wird erneut beurteilt, ob der von ihm erstellte Knoten die kleinste Sequenznummer unter den gesperrten untergeordneten Knoten hat. Wenn ja, wird die Sperre erhalten. Wenn nicht, wird die Sperre erhalten. Wiederholen Sie dann die obigen Schritte, um weiterhin einen Knoten zu erhalten, der kleiner als Sie selbst ist, und registrieren Sie sich für die Überwachung.

8.2 Curator implementiert die verteilte Sperr-API

In Curator gibt es fünf Sperrschemata:

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

8.3 Verteilter Schlosskasten

Simulieren Sie das Ticket-Grabbing-System 12306

public class SaleTickets12306 implements Runnable {
    
    
    private int tickets = 10;  // 模拟的票数
    private InterProcessMutex lock;

    public SaleTickets12306(){
    
    
        // 重试策略
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,10);
        CuratorFramework client = CuratorFrameworkFactory.builder()
                .connectString("192.168.188.128:2181")
                .sessionTimeoutMs(60*1000)
                .connectionTimeoutMs(15*1000)
                .retryPolicy(retryPolicy)
                .namespace("nhk")  // 程序临时创建的命名空间
                .build();
        // 开启连接
        client.start();

        lock = new InterProcessMutex(client,"/lock");

    }

    @Override
    public void run() {
    
    
        while (true) {
    
    
            // 获得锁
            try {
    
    
                lock.acquire(3, TimeUnit.SECONDS);
                if (tickets > 0) {
    
    
                    System.out.println(Thread.currentThread().getName() + ":" + tickets--);
                    Thread.sleep(100);
                }
            } catch (Exception e) {
    
    
                e.printStackTrace();
            } finally {
    
    
                // 释放锁
                try {
    
    
                    lock.release();
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                }
            }

        }
    }
}

public class LookTest {
    
    
    public static void main(String[] args) {
    
    
        SaleTickets12306 saleTickets12306 = new SaleTickets12306();

        // 创建售票平台
        Thread t1 = new Thread(saleTickets12306,"携程");
        Thread t2 = new Thread(saleTickets12306,"铁友");

        t1.start();
        t2.start();
    }
}

Guess you like

Origin blog.csdn.net/weixin_56058578/article/details/132717176
01