zookeeper是一个开源的分布式,基于观察者模式的分布式服务管理框架。
使用场景
统一配置管理、统一命名服务、统一集群管理、软负载均衡、服务器节点动态上下线等
统一配置管理:将配置信息写入zookeeper的一个znode中,各个服务端监听这个znode,znode中内容经过修 改,zookeeper将会通知各个服务器
统一命名服务:对服务/应用进行统一命名,便于识别
统一集群管理:跟配置管理一样,写入znode并且监听,获取实时变化
软负载均衡: 记录每台服务器的访问量,让访问少的去处理新的请求
服务器节点动态上下线:客户端实时洞察服务器上下线变化
zookeeper特点
1、zookeeper有两种角色:主节点(Leader),一群从节点(Follower)
2、集群中只有要一半以上的节点存活,zookeeper集群就能正常服务
3、数据一致性,客户端无论连接到哪个服务器,数据都是一致的
4、实时性,在一定时间范围之内,客户端能读取到最新的数据
5、原子性,更新数据要么全成功,要么失败
6、来自于同一个客户端的更新请求,按照其顺序依次执行
1、将zookeeper的tar包放到liunx系统下
2、解压:
tar -zxvf zookeeper-3.4.10.tar.gz
3、修改配置文件(zookeeper包conf目录下):
mv zoo_sample.cfg zoo.cfg
4、打开zoo.cfg文件修改dataDir路径,zookeeper所在的包路径下+/zkData
5、在zookeeper所在的包路径下创建zkData
mkdir zkData
#启动zookeeper(zookeeper的bin目录下)
./zkServer.sh start
#查看状态
./zkServer.sh status
#启动客户端
./zkCli.sh
#退出客户端
quit
#停止zookeeper
./zkServer.sh stop
zookeeper集群
1、在zkData目录下创建一个myid的文件:touch myid
2、在myid中添加对应的编号 : 1
3、剩下的服务器按照上面的顺序搭, 不过另外两台服务器的myid内容为 2 、 3
4、进入zoo.cfg文件,修改dataDir路径,以及添加如下配置
server.1=第一台服务器的主机名:2888:3888
server.2=第二台服务器的主机名:2888:3888
server.3=第三台服务器的主机名:2888:3888
#查看当前znode内容
ls /
#查看当前节点详细数据
ls2 /
#创建节点
create /zg "zg"
create /zg/bj "bj"
#创建短暂节点
create -e /zg/sh "sh"
#创建序号节点
create -s /zg/sz "sz"
#获取节点值
get /zg
#修改节点
set /zg/bj "sdbj"
#删除节点
delete /zg/jiujiuliu
#递归删除节点
rmr /zg/jiaban
#查看节点状态
stat /zg
添加pom文件
<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>对应的zookeeper版本号</version>
</dependency>
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
创建zookeeper客户端
public static final String connectString = "主机名1:2181,主机名2:2181,主机名3:2181";
public static final Integer sessionTimeout = 2000;
public static final String parNode = "/servers";
public void serverd() throws Exception {
zooKeeper = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
}
});
}
添加节点
public void cli() throws KeeperException, InterruptedException {
// 参数一:路径 参数二:值 参数三:节点权限 参数四:节点类型
String s = zooKeeper.create("/m", "mengyizhou".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println("s = " + s);
}
删除节点
public void delnode() throws KeeperException, InterruptedException {
//参数一:路径 参数二:版本
zooKeeper.delete("/m",0);
}
判断节点那是否存在
public void judge() throws KeeperException, InterruptedException {
// 参数一:路径 参数二:是否监听
Stat exist = zooKeeper.exists("/meng", false);
System.out.println(exist == null ? "exists" : "no exists");
}
监听子节点变化
@Before
public void serverd() throws Exception {
zooKeeper = new ZooKeeper(Zenum.connectString, Zenum.sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
try {
System.out.println("============");
//参数一 : 路径(获取该路径下所有子节点) 参数二:是否监听
List<String> list = zooKeeper.getChildren("/", true);
list.forEach((item)->{
System.out.println(item);
});
System.out.println("============");
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
/**
* 监控节点变化
* @throws KeeperException
* @throws InterruptedException
*/
@Test
public void getChildrens() throws KeeperException, InterruptedException {
Thread.sleep(5000000);
}
创建节点
监听服务器动态上下线
//服务器
public class Server {
public ZooKeeper zooKeeper;
public static final String connectString = "主机名1:2181,主机名2:2181,主机名3:2181";
public static final Integer sessionTimeout = 2000;
public static final String parNode = "/servers";
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
//连接到zookeeper集群
Server server = new Server();
server.Connect();
//注册服务器
server.register(args[0]);
//逻辑代码
server.business(args[0]);
}
/**
* 连接到客户端
*/
public void Connect() throws IOException {
zooKeeper = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
}
});
}
/**
*注册服务器
*/
public void register(String host) throws KeeperException, InterruptedException {
String s = zooKeeper.create(Zenum.parNode+"/server", host.getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println("s = " + s);
}
/**
* 业务
*/
public void business(String host) throws InterruptedException {
System.out.println(host);
Thread.sleep(50000000);
}
}
//客户端
public class Client {
private ZooKeeper zooKeeper;
public static final String connectString = "主机名1:2181,主机名2:2181,主机名3:2181";
public static final Integer sessionTimeout = 2000;
public static final String parNode = "/servers";
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
//创建连接
Client client = new Client();
client.Connect();
//获取服务列表
client.getServers();
//业务
client.business();
}
/**
* 连接到客户端
*/
public void Connect() throws IOException {
zooKeeper = new ZooKeeper(Zenum.connectString, Zenum.sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
try {
getServers();
}catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* 获取服务列表
*/
public void getServers() throws KeeperException, InterruptedException {
//服务子节点信息
List<String> childrens = zooKeeper.getChildren(Zenum.parNode, true);
List<String> list = new ArrayList<>();
childrens.forEach((item)->{
try {
byte[] data = zooKeeper.getData(Zenum.parNode + "/" + item, false, null);
list.add(data.toString());
} catch (Exception e) {
e.printStackTrace();
}
});
System.out.println("======================================");
System.out.println(list);
}
/**
* 业务
*/
public void business() throws InterruptedException {
Thread.sleep(50000000);
}
}
节点类型
持久节点:
客户端和服务器之间断开连接,节点依旧存在
短暂节点:
客户端和服务器之间断开连接,节点就会删除
持久节顺序编号节点:
客户端和服务器之间断开连接,节点依旧存在,zookeeper给该节点进行顺序编号
短暂节顺序编号节点:
客户端和服务器之间断开连接,节点就会删除,zookeeper给该节点进行顺序编号
监听器原理
1、在main线程中创建zookeeper客户端,
2、客户端会创建两个线程,connect(负责网络通信),Listener(负责监听)
3、通过connect线程将监听事件发送给zookeeper
4、zookeeper将监听到的事件添加到列表中
5、当数据发生变化时
选举机制
集群中只有要一半以上的节点存活,zookeeper集群就能正常服务,所有zookeeper适合安装奇数台服务器
zookeeper工作时,只要一个leader,其他的为follower,leader是通过选举机制临时产生的。
选举leader采用投票机制,得票数超过一半以上的节点数量的服务器为leader
写入流程
1、当客户端向服务器请求写入
2、判断该服务器是否为leader,如果不是leader将请求进一步转发给leader
3、leader将写入的请求广播给各个服务器,各个服务器将该写入请求加入待写队列,向leader发送成功信息
4、当leader收到半数以上的成功信息,会向各个服务器发送提交信息,各个服务器落实队列写请求,写成功了
5、服务器会通知客户端写入数据成功
ss方法
[外链图片转存中…(img-AgOIzRep-1622556086032)]
选举机制
集群中只有要一半以上的节点存活,zookeeper集群就能正常服务,所有zookeeper适合安装奇数台服务器
zookeeper工作时,只要一个leader,其他的为follower,leader是通过选举机制临时产生的。
选举leader采用投票机制,得票数超过一半以上的节点数量的服务器为leader
写入流程
1、当客户端向服务器请求写入
2、判断该服务器是否为leader,如果不是leader将请求进一步转发给leader
3、leader将写入的请求广播给各个服务器,各个服务器将该写入请求加入待写队列,向leader发送成功信息
4、当leader收到半数以上的成功信息,会向各个服务器发送提交信息,各个服务器落实队列写请求,写成功了
5、服务器会通知客户端写入数据成功