Java学习(第三阶段模块二)2020/6/18-2020/6/26

任务一:Zookeeper简介

顺利完成第三阶段模块一的学习,正式进入第三阶段模块二。
由于自己各方面的原因拖后了进度,会再接再励赶上进度的。

1. ZooKeeper如何解决分布式系统面临的问题

ZooKeeper最为主要的使用场景是作为分布式系统的分布式协同服务,Zookeeper对分布式系统的协调,使用的是共享存储。大部分分布式系统中出现的问题,都源于信息共享出了问题,如果各个节点间信息不能及时共享和同步,那么就会在协作过程中产生各种问题。ZooKeeper解决协同问题的关键就是在于保证分布式系统信息的一致性。

2. ZooKeeper的基本概念

ZooKeeper是一个开源的分布式协调服务,其设计的目的是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一些简单的接口提供给用户使用。ZooKeeper是一个典型的分布式数据一致性的解决方案,分布式程序可以基于它实现诸如数据订阅/发布、负载均衡、命名服务、集群管理、分布式锁和分布式队列等功能。
1.集群角色
通常在分布式系统中,构成集群的每一台机器都有自己的角色,最典型的集群就是Master/Slave模式(主备模式),此情况下就是把所有能够处理写操作的机器称为Master机器,把所有通过异步复制方式获取最新数据,并提供读服务的机器称为Slave机器。
而在ZooKeeper中,这些概念被颠覆了,它没有沿用传统的Master/Slave概念,而是引入了Leader、Follower、Observer三种角色。ZooKeeper集群中的所有机器通过Leader选举来选定一台Leader机器,Leader服务器为客户端提供读服务和写服务,除Leader外,其他机器包括Follower和Observer的机器,它们都能提供读服务,唯一的区别就在于Observer不参与Leader选举过程,不参与写操作的过半写成功策略。因此Ovserver可以在不影响写性能的情况下提升集群的性能。
2.会话
Session指客户端会话,一个客户端连接是指客户端和服务端之间的一个TCP长连接,ZooKeeper对外的服务端口默认问2181,客户端启动的时候,首先会与服务器建立一个TCP长连接,从第一次连接建立开始,客户端会话的生命周期也开始了,通过这个连接,客户端能够心跳监测与服务器保持有效的会话,也能够向ZooKeeper服务器发送请求并接受响应,同时还能够通过该连接接受来自服务器的Watch事件通知。
3.数据节点(Znode)
在谈到分布式的时候,我们通常说的节点是指组成集群的每一台机器,然而,在ZooKeeper中,节点分为两类,第一类同样是指构成集群的机器,我们称之为机器节点,第二类则是指数据模型中的数据单元,我们称之为数据节点–ZNode。ZooKeeper将所有的数据存储在内存中,数据模型是一颗树(ZNode Tree),由斜杠(/)进行分割的路径,就是一个Znode,例如/app/path1。每个ZNode上都会保存自己的数据内容,同时还会保存一系列属性信息。
4.版本
ZooKeeper的每个Znode上都会存储数据,对于每个ZNode,ZooKeeper都会为其维护一个叫做Stat的数据结构,Stat记录了这个ZNode的三个数据版本,分别是version(当前ZNode的版本)、cversion(当前ZNode子节点的版本)、aversion(当前ZNode的ACL版本)
5.Watcher(事件监听器)
Watcher(事件监听器),是ZooKeeper中一个很重要的特性,ZooKeeper允许用户在指定节点上注册一些Watcher,并且在一些特定事件触发的时候,ZooKeeper服务端会将事件通知到感兴趣的客户端,该机制是ZooKeeper实现分布式协调服务的重要特性。
6.ACL
ZooKeeper采用ACL(Access Control Lists)策略来进行权限控制,其定义了如下五种权限:
CREATE:创建子节点的权限
READ:获取节点数据和子节点列表的权限
WRITE:更新节点数据的权限
DELETE:删除子节点的权限
ADMIN:设置节点ACL的权限
其中需要注意的是,CREATE和DELETE这两种权限都是针对子节点的权限控制。

3. ZooKeeper环境搭建

//解压缩
tar -zxvf zookeeper-3.4.14.tar.gz
//修改配置文件
cd zookeeper-3.4.14
mkdir data
cd conf
mv zoo_sample.cfg zoo.cfg
dataDir=/root/zookeeper-3.4.14/data
//启动
./zkServer.sh start
//关闭
./zkServer.sh stop
//查看状态
./zkServer.sh status

clientPort
如果在一台机器上部署多个server,那么每一台机器都要有不同的clientPort,比如server1是2181,server2是2182,server3是2183
dataDir和dataLogDir
dataDir和dataLogDir区分,将数据文件和日志文件分开存放,同时每个server的这两个变量所对应的路径都是不用的
server.X和myid
server.X这个数字就是对应data/myid中的数字,在3个server的myid文件中分别写入1,2,3,那么每个server中共的zoo.cfg都配置server.1 server.2 server.3

clientPort=2181
dataDir=/zkcluster/zookeeper01/data
dataLogDir=/zkcluster/zookeeper01/data/logs

clientPort=2182
dataDir=/zkcluster/zookeeper02/data
dataLogDir=/zkcluster/zookeeper02/data/logs

clientPort=2183
dataDir=/zkcluster/zookeeper03/data
dataLogDir=/zkcluster/zookeeper03/data/logs
#server.服务器ID=服务器IP地址:服务器之间通信端⼝:服务器之间投票选举端⼝
server.1=10.211.55.4:2881:3881
server.2=10.211.55.4:2882:3882
server.3=10.211.55.4:2883:3883

4. ZooKeeper基本使用

在这里插入图片描述
在ZooKeeper中,每一个数据节点都是一个ZNode,上图根目录下有两个节点,分别为/app1和/app2,其中app1下面又有三个子节点,所有ZNode按层次进行组织,形成一棵树,ZNode的节点路径标识方式和Unix文件系统路径非常相似,都是由一系列使用斜杠(/)进行分割的路径表示,开发人员可以向这个节点写入数据,也可以在这个节点下面创建节点。
ZNode的类型
ZooKeeper的节点类型可以分为三类:
持久性节点(Persistent)
临时性节点(Ephemeral)
顺序性节点(Sequential)
在开发中创建节点的时候通过组合可以生成以下四种节点类型:持久节点、持久顺序节点、临时节点、临时顺序节点。不同的节点类型会有不同的生命周期。
1.持久节点:是ZooKeeper中最常见的一种节点类型,所谓持久节点,就是指节点创建后会一直存在在服务器上,直到删除节点的操作主动清除。
2.持久顺序节点:就是有顺序的持久节点,节点特性和持久节点是一样的,只是额外特性表现在顺序上。顺序特性实质是在创建节点的时候,会在节点后面加上一个数字后缀,来表示其顺序。
3.临时节点:就是会被自动清理的节点,它的生命周期和客户端会话绑在一起,客户端会话结束,节点会被删除掉。与持久节点不同的是,临时节点不能创建子节点。
4.临时顺序节点:就是有顺序的临时节点,和持久顺序节点相同,在其创建的时候会在名字后面加上数字后缀。
事务ID
在ZooKeeper中,事务是指能够改变ZooKeeper服务器状态的操作,我们也称之为事务操作或更新操作,一般包括数据节点创建与删除、数据节点内容更新等操作。对于每一个事务请求,ZooKeeper都会为其分配一个全局唯一的事务ID,用ZXID表示,通常是一个64位的数字。每一个ZXID对应一次更新操作,从这些ZXID中可以间接识别出ZooKeeper处理这些更新操作请求的全局顺序。
在这里插入图片描述
Watcher--数据变更通知
在这里插入图片描述
ACL--保障数据安全
在ZooKeeper中,提供了一套完整的ACL(Access Control List)权限控制机制来保障数据的安全。
权限模式(Scheme)、授权对象(ID)、权限(Permission),通常使用Scheme:id:permission来标识一个有效的ACL信息。
权限模式用来确定权限验证过程中使用的检验策略,有如下四种模式:
1.IP :按照网段进行配置。
2.Digest username:password
3.World: 最开放的权限控制模式,可以看作是最特殊的Digest模式,即world:anyone
4.Super:超级用户的意思,是一种特殊的Digest模式。在Super模式下,超级用户可以对任意ZooKeeper上的数据节点进行任何操作。
授权对象:ID
1.IP:通常是一个IP地址或IP段
2.Digest:自定义,通常是username:BASE64(SHA-1(username:password))
3.World:只有一个ID:anyone
4.Super:超级用户
权限:
五大类:
1.CREATE©
2.DELETE(D)
3.READ®
4.WRITE(W)
5.ADMIN(A)

./zkcli.sh 连接本地的zookeeper服务器
./zkCli.sh -server ip:port 连接指定的服务器
create [-s][-e] path data acl
//其中,-s或-e分别指定节点特性,顺序或临时节点,若不指定,则创建持久节点;acl⽤来进⾏权限控制。
ls path
//其中,path表示的是指定数据节点的节点路径
get path
set path data [version]
delete path [version]

4. ZooKeeperAPI的使用

<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.14</version>
</dependency>
//建立会话
public class CreateSession implements Watcher {
//countDownLatch这个类使⼀个线程等待,主要不让main⽅法结束
private static CountDownLatch countDownLatch = new CountDownLatch(1);
public static void main(String[] args) throws InterruptedException,
IOException {
/*
客户端可以通过创建⼀个zk实例来连接zk服务器
new Zookeeper(connectString,sesssionTimeOut,Wather)
connectString: 连接地址:IP:端⼝
sesssionTimeOut:会话超时时间:单位毫秒
Wather:监听器(当特定事件触发监听时,zk会通过watcher通知到客户端)
*/
ZooKeeper zooKeeper = new ZooKeeper("10.211.55.4:2181", 5000, new
CreateSession());
System.out.println(zooKeeper.getState());
countDownLatch.await();
//表示会话真正建⽴
System.out.println("=========Client Connected to
zookeeper==========");
}
// 当前类实现了Watcher接⼝,重写了process⽅法,该⽅法负责处理来⾃Zookeeper服务端的watcher通知,在收到服务端发送过来的SyncConnected事件之后,解除主程序在CountDownLatch上的等待阻塞,⾄此,会话创建完毕
public void process(WatchedEvent watchedEvent) {
//当连接创建了,服务端发送给客户端SyncConnected事件
if(watchedEvent.getState() == Event.KeeperState.SyncConnected){
countDownLatch.countDown();
}
}
}
//创建节点
public class CreateNote implements Watcher {
//countDownLatch这个类使⼀个线程等待,主要不让main⽅法结束
private static CountDownLatch countDownLatch = new CountDownLatch(1);
private static ZooKeeper zooKeeper;
public static void main(String[] args) throws Exception {
zooKeeper = new ZooKeeper("10.211.55.4:2181", 5000, new CreateNote());
countDownLatch.await();
}
public void process(WatchedEvent watchedEvent) {
//当连接创建了,服务端发送给客户端SyncConnected事件
if(watchedEvent.getState() == Event.KeeperState.SyncConnected){
countDownLatch.countDown();
}
//调⽤创建节点⽅法
try {
createNodeSync();
} catch (Exception e) {
e.printStackTrace();
}
}
private void createNodeSync() throws Exception {
/**
* path :节点创建的路径
* data[] :节点创建要保存的数据,是个byte类型的
* acl :节点创建的权限信息(4种类型)
* ANYONE_ID_UNSAFE : 表示任何⼈
* AUTH_IDS :此ID仅可⽤于设置ACL。它将被客户机验证的ID替
换。
* OPEN_ACL_UNSAFE :这是⼀个完全开放的ACL(常⽤)-->
world:anyone
* CREATOR_ALL_ACL :此ACL授予创建者身份验证ID的所有权限
* createMode :创建节点的类型(4种类型)
* PERSISTENT:持久节点
* PERSISTENT_SEQUENTIAL:持久顺序节点
* EPHEMERAL:临时节点
* EPHEMERAL_SEQUENTIAL:临时顺序节点
String node = zookeeper.create(path,data,acl,createMode);
*/
String node_PERSISTENT = zooKeeper.create("/lg_persistent", "持久节点内
容".getBytes("utf-8"), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
String node_PERSISTENT_SEQUENTIAL =
zooKeeper.create("/lg_persistent_sequential", "持久节点内容".getBytes("utf-8"),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
String node_EPERSISTENT = zooKeeper.create("/lg_ephemeral", "临时节点内
容".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
System.out.println("创建的持久节点是:"+node_PERSISTENT);
System.out.println("创建的持久顺序节点是:"+node_PERSISTENT_SEQUENTIAL);
System.out.println("创建的临时节点是:"+node_EPERSISTENT);
}
}
//获取节点数据
public class GetNoteData implements Watcher {
//countDownLatch这个类使⼀个线程等待,主要不让main⽅法结束
private static CountDownLatch countDownLatch = new CountDownLatch(1);
private static ZooKeeper zooKeeper;
public static void main(String[] args) throws Exception {
zooKeeper = new ZooKeeper("10.211.55.4:2181", 10000, new
GetNoteDate());
Thread.sleep(Integer.MAX_VALUE);
}
public void process(WatchedEvent watchedEvent) {
//⼦节点列表发⽣变化时,服务器会发出NodeChildrenChanged通知,但不会把变化情况告诉给客户端
// 需要客户端⾃⾏获取,且通知是⼀次性的,需反复注册监听
if(watchedEvent.getType() ==Event.EventType.NodeChildrenChanged){
//再次获取节点数据
try {
List<String> children =
zooKeeper.getChildren(watchedEvent.getPath(), true);
System.out.println(children);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//当连接创建了,服务端发送给客户端SyncConnected事件
if(watchedEvent.getState() == Event.KeeperState.SyncConnected){
try {
//调⽤获取单个节点数据⽅法
getNoteDate();
getChildrens();
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private static void getNoteData() throws Exception {
/**
* path : 获取数据的路径
* watch : 是否开启监听
* stat : 节点状态信息
* null: 表示获取最新版本的数据
* zk.getData(path, watch, stat);
*/
byte[] data = zooKeeper.getData("/lg_persistent/lg-children", true,
null);
System.out.println(new String(data,"utf-8"));
}
private static void getChildrens() throws KeeperException,
InterruptedException {
/*
path:路径
watch:是否要启动监听,当⼦节点列表发⽣变化,会触发监听
zooKeeper.getChildren(path, watch);
*/
List<String> children = zooKeeper.getChildren("/lg_persistent", true);
System.out.println(children);
}
}
//修改节点数据
public class updateNote implements Watcher {
private static ZooKeeper zooKeeper;
public static void main(String[] args) throws Exception {
zooKeeper = new ZooKeeper("10.211.55.4:2181", 5000, new updateNote());
Thread.sleep(Integer.MAX_VALUE);
}
public void process(WatchedEvent watchedEvent) {
//当连接创建了,服务端发送给客户端SyncConnected事件
try {
updateNodeSync();
} catch (Exception e) {
e.printStackTrace();
}
}
private void updateNodeSync() throws Exception {
/*
path:路径
data:要修改的内容 byte[]
version:为-1,表示对最新版本的数据进⾏修改
zooKeeper.setData(path, data,version);
*/
byte[] data = zooKeeper.getData("/lg_persistent", false, null);
System.out.println("修改前的值:"+new String(data));
//修改 stat:状态信息对象 -1:最新版本
Stat stat = zooKeeper.setData("/lg_persistent", "客户端修改内
容".getBytes(), -1);
byte[] data2 = zooKeeper.getData("/lg_persistent", false, null);
System.out.println("修改后的值:"+new String(data2));
}
}
//删除节点
public class DeleteNote implements Watcher {
private static ZooKeeper zooKeeper;
public static void main(String[] args) throws Exception {
zooKeeper = new ZooKeeper("10.211.55.4:2181", 5000, new DeleteNote());
Thread.sleep(Integer.MAX_VALUE);
}
public void process(WatchedEvent watchedEvent) {
//当连接创建了,服务端发送给客户端SyncConnected事件
try {
deleteNodeSync();
} catch (Exception e) {
e.printStackTrace();
}
}
private void deleteNodeSync() throws KeeperException, InterruptedException
{
/*
zooKeeper.exists(path,watch) :判断节点是否存在
zookeeper.delete(path,version) : 删除节点
*/
Stat exists = zooKeeper.exists("/lg_persistent/lg-children", false);
System.out.println(exists == null ? "该节点不存在":"该节点存在");
zooKeeper.delete("/lg_persistent/lg-children",-1);
Stat exists2 = zooKeeper.exists("/lg_persistent/lg-children", false);
System.out.println(exists2 == null ? "该节点不存在":"该节点存在");
}
}

任务二:Zookeeper开源客户端

ZKClient是Github上一个开源的ZooKeeper客户端,在ZooKeeper原生API上进行了包装,是一个更易用的客户端,同时,内部还实现了诸如Session超时重连、Watcher反复注册等功能。

<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.2</version>
</dependency>
package com.hust.grid.leesf.zkclient.examples;
import java.io.IOException;
import org.I0Itec.zkclient.ZkClient;
public class CreateSession {
/*
创建⼀个zkClient实例来进⾏连接
注意:zkClient通过对zookeeperAPI内部包装,将这个异步的会话创建过程同步化了
*/
public static void main(String[] args) {
ZkClient zkClient = new ZkClient("127.0.0.1:2181");
System.out.println("ZooKeeper session established.");
}
}
//创建节点
package com.hust.grid.leesf.zkclient.examples;
import org.I0Itec.zkclient.ZkClient;
public class Create_Node_Sample {
public static void main(String[] args) {
ZkClient zkClient = new ZkClient("127.0.0.1:2181");
System.out.println("ZooKeeper session established.");
//createParents的值设置为true,可以递归创建节点
zkClient.createPersistent("/lg-zkClient/lg-c1",true);
System.out.println("success create znode.");
}
}
//删除节点
package com.hust.grid.leesf.zkclient.examples;
import org.I0Itec.zkclient.ZkClient;
public class Del_Data_Sample {
public static void main(String[] args) throws Exception {
String path = "/lg-zkClient/lg-c1";
ZkClient zkClient = new ZkClient("127.0.0.1:2181", 5000);
zkClient.deleteRecursive(path);
System.out.println("success delete znode.");
}
}
//获取子节点
package com.hust.grid.leesf.zkclient.examples;
import java.util.List;
import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.ZkClient;
public class Get_Children_Sample {
public static void main(String[] args) throws Exception {
ZkClient zkClient = new ZkClient("127.0.0.1:2181", 5000);
List<String> children = zkClient.getChildren("/lg-zkClient");
System.out.println(children);
//注册监听事件
zkClient.subscribeChildChanges(path, new IZkChildListener() {
public void handleChildChange(String parentPath, List<String>
currentChilds) throws Exception {
System.out.println(parentPath + " 's child changed,
currentChilds:" + currentChilds);
}
});
zkClient.createPersistent("/lg-zkClient");
Thread.sleep(1000);
zkClient.createPersistent("/lg-zkClient/c1");
Thread.sleep(1000);
zkClient.delete("/lg-zkClient/c1");
Thread.sleep(1000);
zkClient.delete(path);
Thread.sleep(Integer.MAX_VALUE);
}
}
//获取数据
public class Get_Data_Sample {
public static void main(String[] args) throws InterruptedException {
String path = "/lg-zkClient-Ep";
ZkClient zkClient = new ZkClient("127.0.0.1:2181");
//判断节点是否存在
boolean exists = zkClient.exists(path);
if (!exists){
zkClient.createEphemeral(path, "123");
}
//注册监听
zkClient.subscribeDataChanges(path, new IZkDataListener() {
public void handleDataChange(String path, Object data) throws
Exception {
System.out.println(path+"该节点内容被更新,更新后的内容"+data);
}
public void handleDataDeleted(String s) throws Exception {
System.out.println(s+" 该节点被删除");
}
});
//获取节点内容
Object o = zkClient.readData(path);
System.out.println(o);
//更新
zkClient.writeData(path,"4567");
Thread.sleep(1000);
//删除
zkClient.delete(path);
Thread.sleep(1000);
}
}

任务三:ZookeeperZAB协议

任务四:Zookeeper源码分析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕业设计,基于SpringBoot+Vue+MySQL开发的公寓报修管理系统,源码+数据库+毕业论文+视频演示 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本公寓报修管理系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员提高事务处理效率,达到事半功倍的效果。此公寓报修管理系统利用当下成熟完善的Spring Boot框架,使用跨平台的可开发大型商业网站的Java语言,以及最受欢迎的RDBMS应用软件之一的MySQL数据库进行程序开发。公寓报修管理系统有管理员,住户,维修人员。管理员可以管理住户信息和维修人员信息,可以审核维修人员的请假信息,住户可以申请维修,可以对维修结果评价,维修人员负责住户提交的维修信息,也可以请假。公寓报修管理系统的开发根据操作人员需要设计的界面简洁美观,在功能模块布局上跟同类型网站保持一致,程序在实现基本要求功能时,也为数据信息面临的安全问题提供了一些实用的解决方案。可以说该程序在帮助管理者高效率地处理工作事务的同时,也实现了数据信息的整体化,规范化与自动化。 关键词:公寓报修管理系统;Spring Boot框架;MySQL;自动化;VUE
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值