跟孙哥学java
zookeeper简介
集群管理技术
提供了强一致性的保证,基于Paxos算法的ZAB协议保证的数据一致性
顾名思义 zookeeper 就是动物园管理员,他是用来管 hadoop(大象)、Hive(蜜蜂)、pig(小 猪)的管理员, Apache Hbase 和 Apache Solr 的分布式集群都用到了 zookeeper;Zookeeper: 是一个分布式(集群)的、开源的程序协调服务(服务),是 hadoop 项目下的一个子项目。他提供的主要功 能包括:配置管理、名字服务、分布式锁、集群管理。
广泛引用在java开发中:
大数据 hadoop hbase
javaEE dubbo kafka RocketMQ
存在趋势:去zookeeper 。原因:
- 数据一致性的算法不够优秀 (RAFT优于Paxos) 2.完整的整体
zk的功能
注册中心,配置中心,负载均衡,故障转移,分布式锁..... 注册中心:
- 把一组功能相同的rpc服务管理起来,并且给这一组服务命名
- 健康检查(定期检查rpc服务是否正常运行)
- 负载均衡,通过负载均衡选择一个特定的RPC服务(轮询。。。)
注意: 服务发现与注册/注册中心/命名服务 这三者是一样的
zk的替代性产品
consul,etcd ,applo,nacos...
zookeeper的逻辑结构
树形结构
根节点 /
后续接待你 绝对路径 起始 /
树上的节点 名词 znode
zookeeper节点的分类
- 持久节点
- 临时节点
- 有序的持久节点
- 有序的临时节点
zookeeper 物理结构
单机版 standalone
单机版的zk 只能进行测试 而不用用于生产环境。
原因:1. 单点故障 2. 受限硬件资源 (CPU 内存 网络)
集群版
集群zk注意事项:
- zk-server分为 主节点(leader) 从节点(flower) 其他的集群命名一般是主Master从Slave,zk为了与其他Client集群命名产生歧义,所以把主节点叫leader,从节点叫flower
- 如何确定zk集群中节点的身份 (主从) zk使用选举算法(投票)过半数认可,就是主节点
- 主节点作用 : 主节点管理树状结构 增加,删除--->节点 查询节点
- 从节点作用: 主节点修改了树状结构,会同步到从节点,从节点主要负载查询树状
- 注意:如果一半的从节点更新到了最新的数据,那么zk就认可这个操作成功了
- zk集群的容错性:只要集群中过半节点出现问题,zk集群就会中断服务
- zk集群节点数有说明要求:
- 任何个数节点(大于3)都可以作为 zk集群 3....-->zk集群
- 建议zk集群的个数用奇数 。因为就容错性来说,增加一个节点为偶数时,并不会增加系统的可靠性,但是硬件资源占了更多
安装zk、
docker安装
blog.csdn.net/lanse_huanx…
linux安装:
apache归档 archive.apache.org/dist/
下载3.6.1版本和孙哥一样
移动到linux上
tar -zxvf apache-zookeeper-3.6.1-bin.tar.gz
解压缩到当前路径
改个名字
2.配置zookeeper
修改名字
mv conf/zoo_sample.cfg conf/zoo.cfg
进入zoo.cfg
vim zoo.cfg
创建数据文件夹
mkdir /root/zookeeper3.6.1/data
- 启动zk服务
cd bin 查看命令
./zkServer.sh start 启动服务
ps -ef |grep java 查看允许进程
jps 直接查看java进程
看到Quo rumPeerMain 说明启动成功了
./zkServer.sh status /stop 查看状态、停止
客户端访问: bin下面 ./zkCli.sh 连接到zk服务器
集群版zk
- ssh登录 :linux远端登录操作的一种手段,对比其他的登录方式(telnet),ssh登录非常安全,可以防止黑客恶意的劫持。
- ssh登录 :在ssh登录过程中,需要提供用户名、密码。
- ssh免密登录 : 在集群环境下,个个节点间可能需要进行相互通信,设置会相互执行一些命令,那么在执行命令时,需要登录到另一台服务上,才可以执行。按照传统的ssh登录需要提供用户名密码,会造成通信的复杂度,设置需要人工干预,繁琐。所以ssh提供了免密登录,降低服务器之间登录通信的难度。所以在集群环境下搭建ssh免密登录是一种常见的运维手段。
- ssh免密登录运行流程
:::info
免密登录 默认使用 ssh root@blue.huy.com 用户名@主机名
ssh 免密登录:
生成公私钥对
1. 生成公私钥对
ssh-keygen -t rsa
公私钥对的放置的位置 ~/.ssh id_rsa id_rsa.pub
2. 公钥发送给远端的主机
ssh-copy-id 用户名@主机名
公钥存储在远端主机 ~/.ssh
authorized_keys
:::
- 奇数个节点 进行zk集群的安装。3台
- 主机名 克隆 规划 zookeeper1.suns.com 172.16.79.132 zookeeper2.suns.com 172.16.79.133 zookeeper3.suns.com 172.16.79.134
- vim /ect/hosts文件 172.16.79.132 zookeeper1.suns.com 172.16.79.133 zookeeper2.suns.com 172.16.79.134 zookeeper3.suns.com 技巧: scp 个个节点之间数据的复制 scp 本机文件的位置 用户名@主机名:路径 scp /etc/hosts root@zookeeper2.suns.com:/etc
- zk集群搭建
- 安装jdk
- 安装zk
- 解压缩 改名
- 准备数据目录 [区别] root/zookeeper-3.6.1/data 创建myid文件 ---> 1
- 修改配置文件 [区别] conf/zoo.cfg dataDir=/root/zookeeper-3.6.1/data server.1=zookeeper1.suns.com:2888:3888 server.2=zookeeper2.suns.com:2888:3888 server.3=zookeeper3.suns.com:2888:3888 4.集群的复制 scp -r /root/zookeeper-3.6.1/ root@zookeeper3.suns.com:/root 5.修改myid文件 zookeeper2 myid 2 zookeeper3 myid 3
- 每一个节点上面 启动zk服务 ./zkServer.sh start ./zkServer.sh status
四种节点类型:
:::info
- 持久节点 如果zk client在zk树状结构上创建了持久节点,那么client在与zk服务端断掉连接后,这个节点还会永久的放置在zk的树状结构上。
- 临时节点 如果zk client在zk树状结构上创建了临时节点,那么client在与zk服务端断掉连接后,这个节点就会从zk的树状结构上移除。
- 持久有序节点 首先他是持久节点,同时这个节点的名字上还会有一个序号,且这个序号是zk集群中唯一(自增)
- 临时有序节点 首先他是持久节点,同时这个节点的名字上还会有一个序号,且这个序号是zk集群中唯一(自增) ::: 注意事项: 正对于有序节点序号,他是全局唯一的,累加的,和节点时不是持久,临时没有关系
zookeeper客户端使用
zk cli命令
- 客户端连接集群的命令: ./zkCli.sh -server ip|hostname
- 退出客户端 quit
- 创建 节点:
create /cloudcreate create /cloudcreate/java 不能越级创建节点
-
创建节点 create /cloudcreate
create /cloudcreate/java
不能越级创建节点
-
创建有数据的节点 create /cloudcreate/java/teacher "suns"
get /cloudcreate/java/teacher 查看数据
3。创建临时节点
create -e /cloudcreate/python
当我们退出,重新登录
临时节点就没有了
4.创建持久有序节点
create -s /xx/xx
可以重复创建,因为它可以给节点编号
5.创建临时有序节点 create -e -s /xx/xx
同一个路径下 有序节点的编号是累加 不同路径下的 有序节点的编号 互不影响
- 修改
set /xx/xx xxx
- 查看
get /xx/xx
-
删除
delete 删除某一个具体的路径,如果这个路径下面还有子路径,不能够删除
deleteall 删除某一个具体的路径,如果这个路径下面还有子路径,能够删除
- ls
- 客户端监听的操作 [重要]
1.监听树 相关处理
树结构的变化(插入节点,删除节点)
ls -w /createcloud ---->监听createcloud目录下的节点的变化
w--->watch
ls -w /createcloud 监听器 是一次性的行为,如果反复监控,需要注册
2.节点数据的变化
get -w /createcloud /techer
java代码
- 准备 zk的gui工具 (图形界面)
PrettyZoo github.com/vran-dev/Pr… IDEA插件
修改/创建数据方便
搭建开发环境[依赖jar]
zk 的java代码工具 ,zk的java驱动
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.6.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
java代码
1.创建节点
public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
//zk集群中的每一个节点的ip:port
// IDEA所在的机器中,进行主机映射
// win c:\windows\system32\drivers\etc
String connectionString = "8.130.113.137:2181";
int sessionTimeout = 20000;
zooKeeper = new ZooKeeper(connectionString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
/* try {
List<String> children = zooKeeper.getChildren("/suns", true);
for (String child : children) {
System.out.println("child = " + child);
}
} catch (KeeperException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}*/
}
});
zooKeeper.create("/suns","xiaohei".getBytes(Charset.defaultCharset()), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
}
}
超时时间一定要大一点,我一开始设置2000一直报错,后面改成20000就运行成功了
//2.创建节点,无数据
zooKeeper.create("/xiaoxiao",null,ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL);
//3.创建临时节点
zooKeeper.create("/xiaoxiao",null,ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL);
//4.修改节点
zooKeeper.setData("/xiaohei","111".getBytes(Charset.defaultCharset()),-1);
//5.查询
fale--->不加监控
List<String> children = zooKeeper.getChildren("/", false);
for (String child : children) {
System.out.println("child="+child);
}
//6.删除
zooKeeper.delete("/xiaohei",-1);
不能删除多级目录,
zooKeeper.delete("/a1",-1);
怎么实现多级删除? 自己封装递归,curator API
监听:
zooKeeper = new ZooKeeper(connectionString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
try {
//监听只能监听一次,所以再次设置监听
List<String> children = zooKeeper.getChildren("/suns", true);
for (String child : children) {
System.out.println("child = " + child);
}
} catch (KeeperException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
//监听
zooKeeper.getChildren("/suns",true);
System.in.read();
注意:每一次监听到都要重复注册,只能监听当前路径的下一级,后续的子集无法监听
//7.判断一个节点未来是否存在
//7.判断一个节点是否存在
zooKeeper.exists("/xiaohei",new Watcher(){
@Override
public void process(WatchedEvent watchedEvent) {
System.out.println("这个节点是存在的");
}
});
System.in.read();
//8.判断节点现在 是否存在
Stat exists = zooKeeper.exists("/suns", false);
System.out.println(exists.toString());
Curator API使用
zookeeper原生的api操作起来比较麻烦 Curator是一个操作ZK主流的API 基本概念---是apache开源顶级项目 curator.apache.org/ highlevel --->更高级的API(对zk基本API的封装)
特点--->解决了原始zk进行监听处理,繁琐不合理的问题。
比如
1.不进行监听都需要提供一个Watch
2.不能创建/删除多级目录
3.不能监听层级目录、
4.Curator 内部封装了zookeeper应用场景
1.master选举机制。2.分布式锁 3.分布式计数器...
Curator->ZK类似于Redsiion->Redis
5.Curator完善了客户端操作的可用性
搭建环境
引入依赖
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.2.1</version>
</dependency>
增删改查编码
核心对象 ,CuratorFramework
//1.设置client重试-->保证代码可靠性
ExponentialBackoffRetry backoffRetry=new ExponentialBackoffRetry(100,3,3);
形参:
baseSleepTimeMs – 重试之间等待的初始时间
maxRetries – 重试的最大次数
maxSleepMs – 每次重试时休眠的最长时间(以毫秒为单位)
connectString – 要连接的服务器列表
sessionTimeoutMs – 会话超时
connectionTimeoutMs – 连接超时
retryPolicy – 要使用的重试策略
//使用 1:创建
client.create().forPath("/xiaohei");
没有指定数据,默认会把客户端的ip地址作为目录数据设置
//2.创建目录,同时指定目录的数据
client.create().forPath("/xiaoxiao","1111".getBytes(Charset.defaultCharset()));
//3.创建多级目录
client.create().creatingParentsIfNeeded().
forPath("/a1/a2/a3","数据".getBytes(Charset.defaultCharset()));
//4.创建不同类型的节点 withMode()-->选择类型
client.create().creatingParentsIfNeeded()
.withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath("/xiaohua");
_//5.删除
_client.delete().forPath("/xiaohua");
如果这个节点/xiaohua 是有序节点必须带上它的序号
也就是
client.delete().forPath("/xiaohua0000000010");
_//5.删除多级 默认delete()删除只能删除单极目录 _client.delete().deletingChildrenIfNeeded().forPath("/a1");
_//6.修改 _client.setData().forPath("/xiaoxiao","改数据".getBytes(Charset.defaultCharset()));
_//7,查询
_List list = client.getChildren().forPath("/");
for (String s : list) {
System.out.println(s);
}
//7,查询数据
byte[] bytes = client.getData().forPath("/xiaohei");
System.out.println(new String(bytes));
curator监听操作
CuratorCache---> 缓存节点的原始数据/缓存节点的原始路径
CuratorCacheListener-->监听器--->业务处理 (观察者模式)
1.NodeCacheListenter--->监听数据变化
2.PathChildrenCacheListenter---> 监听路径变化 (监听子路径的变换,多级)
3.TreeCacheListenter--> 监听路径变换 监听父亲 /z1/z2 -->可以监听z2,上面的不能监听z2
Listener 注册 到 Cache
监听节点值的变换--
public class TestNodeCacheListener1 {
public static void main(String[] args) throws IOException {
ExponentialBackoffRetry backoffRetry=new ExponentialBackoffRetry(1000,3,1000);
String connectString="xxxx:2181";
CuratorFramework client= CuratorFrameworkFactory.newClient(connectString,50000,50000,backoffRetry);
client.start();
CuratorCache curatorCache=CuratorCache.build(client,"/xiaoxiao");
CuratorCacheListener curatorCacheListener=CuratorCacheListener.builder().forNodeCache(new NodeCacheListener() {
@Override
public void nodeChanged() throws Exception {
System.out.printf("node value is change");
}
}).build();
curatorCache.listenable().addListener(curatorCacheListener);
curatorCache.start();
System.in.read();
}
}
监听数据变换--
public class TestNodeCacheListener2 {
public static void main(String[] args) throws IOException {
ExponentialBackoffRetry backoffRetry=new ExponentialBackoffRetry(1000,3,1000);
String connectString="xxxx:2181";
CuratorFramework client= CuratorFrameworkFactory.newClient(connectString,50000,50000,backoffRetry);
client.start();
CuratorCache curatorCache=CuratorCache.build(client,"/xiaoxiao");
CuratorCacheListener curatorCacheListener=CuratorCacheListener.builder().forChanges(new CuratorCacheListenerBuilder.ChangeListener() {
@Override
public void event(ChildData oldNode, ChildData node) {
byte[] nodeData = oldNode.getData();
byte[] newData = node.getData();
System.out.println("oldNode value is"+new String(nodeData));
System.out.println("newNode value is"+new String(newData));
}
}).build();
curatorCache.listenable().addListener(curatorCacheListener);
curatorCache.start();
System.in.read();
}
}
发现curator监听过程中---大部分代码都是相同的--->如何封装??-->模板设计模式
//监听 子路径的变换 (子,孙。。。)forPathChildrenCache
public class TestPathChildrenCacheListener {
//监听 子路径的变换 (子,孙。。。)
public static void main(String[] args) throws IOException {
ExponentialBackoffRetry backoffRetry=new ExponentialBackoffRetry(1000,3,1000);
String connectString="8.130.113.137:2181";
CuratorFramework client= CuratorFrameworkFactory.newClient(connectString,50000,50000,backoffRetry);
client.start();
CuratorCache curatorCache=CuratorCache.build(client,"/z1/z2");
CuratorCacheListener curatorCacheListener=CuratorCacheListener.builder().forPathChildrenCache("/z1/z2", client, new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {
System.out.println("chlidrenPath change");
}
}).build();
curatorCache.listenable().addListener(curatorCacheListener);
curatorCache.start();
System.in.read();
}
}
设计与开发注册中心
注册中心 soa 微服务架构中的作用?
管理服务 RPC 一个集群
如何设计java代码?
服务发现 1.client发现服务 -->从注册中心获得服务的可用列表 2.获取可用的服务列表后,选择一个服务节点进行访问--负载均衡 服务列表的监控
ZK集群的选举过程[补充]
- zk 基本概念
逻辑概念 4种节点类型 物理概念 单机 集群版
- zk客户端
cli
java zookeeper
curator
- zk应用常见
注册中心(服务注册,服务发现)
- 集群zk
初始情况下,没有选举之前,zk集群中所有的节点,每个节点的状态都叫做looking 选举的时候,如果集群中国版节点,选举一个节点作为主节点,那么他就是主节点,其他的节点就是从节点 主节点--> Leader 从节点-->Flower [跟随者] Observer[观察者] 两种从节点的类型 Flower与Observer的区别 :Observer只同步数据,不参与选举
选举过程的核心内容--->投票依据
-
id 服务器 ---> data-myid文件
- id小的服务器 会把票投给id大的服务器
-
zxid (zookeeper transaction id zk事务id)
zxid:zk会对每一个写操作 都会记录一个zxid (在那个节点处理的,就会记录在那个节点中) zxid 64位整数 递增 唯一 zxid小的节点 投票---> zxid大的节点
id和zxid的优先级 --> zxid>id
- epoch 时代纪元 -->投票的轮数
投票论数高的最终决定-->投票依据 优先技术 epoch>zxid>id
- 投票的两个维度
1.集群初始化--->初始选举
2.集群运行中-->重新选举->主出现问题的情况
集群启动,主宕机了,重新选主
-
Split-Brains 脑裂
在主从集群中,经常发生----> 出现两个大脑,两个主节点(Leader,Master) 1.zk集群怎么产生脑裂?? zk集群中,出现了多个分区,只有在分区内部的zk节点才能互相通信,并且主节点所在的分区所含的节点较少,而另外一个分区存在原有集群过半的zk节点,在第二个分区满足zk集群运行条件,会进行选举,进而在第二个分区又出现了一个主。过一会之后,两个分区的网络通信恢复之后,就出现了两个主,发生了脑裂 怎么解决的? 出现了两个主,又会进行一次选举,会根据epoch这个条件进行选举,因为第二个分区的主的epoch>第一个分区的主节点,所以第二个分区的主当选主节点