一,前言
与redis的联系
redis 单实例,数据在内存-读取快
鉴于雪崩击穿等问题 ->复制到集群 HA sentinel 哨兵实现高可用
不是绝对的实时同步,可能连最终一致性都算不上
集群模式 分片
完成分布式 协调分布式锁
二,zookeeper(不要把zk当做数据库用)
主从集群 主是单点的(zk运行状态,1.可用状态,有主模型2.不可用状态,无主模型3.不可用状态恢复到可用状态,应该越快越好 200毫秒)
1.leader可能会挂
2.导致服务不可用
3.事实,zk集群及高可用
4.如果有一种方式可以快速的复制出一个leader
一个leader多个follower,客户端想连谁就连谁(每个客户端连接会有一个session,即临时节点)
zookeeper是一个目录结构-->不同目录即为一个节点,可以存数据,官方声明为1M->统一配置管理1.2.3.4.
持久节点
临时节点-->session
序列节点(序列化,要么是持久节点要么是临时节点)
1.统一配置管理<-1M数据 |
分布式锁,临时节点 |
client代码实现 |
|
2.分组管理<--path结构 |
|
锁依托一个父节点,具备 -s |
队列式事务锁 |
3.统一命名<--sequential |
|
代表父节点下可以有多把锁 |
|
4.同步<--临时节点 |
HA 选主 |
|
|
担保
- ZooKeeper非常快速且非常简单。但是,由于其目标是作为构建更复杂的服务(例如同步)的基础,因此它提供了一组保证。这些是:
- 顺序一致性-来自客户端的更新将按照其发送顺序进行应用。
- 原子性-更新成功或失败。没有部分结果。
- 统一视图-无论客户端连接到哪个服务器,客户端都将看到相同的服务视图。
- 可靠性-应用更新后,此更新将一直持续到客户端覆盖更新为止。
- 及时性-确保系统的客户视图在特定时间范围内是最新的。
三 安装启动
- 1.先安装jdk (下载,解压,配置环境变量)
export JAVA_HOME=/app/jdk1.8.0_231/
export CLASSPATH=.:$JAVA_HOME/lib.dt.jar:$JAVA_HOME/lib/tools.jar
export PATH=$PATH:$JAVA_HOME/bin
Java-version验证是否安装成功
- 2.再zookeeper(下载,解压,配置环境变量)
export JAVA_HOME=/app/jdk1.8.0_231/
export CLASSPATH=.:$JAVA_HOME/lib.dt.jar:$JAVA_HOME/lib/tools.jar
export ZOOKEEPER_HOME=/app/zookeeper/apache-zookeeper-3.5.5-bin/
export PATH=$PATH:$JAVA_HOME/bin:$ZOOKEEPER_HOME/bin
输入zk按tab键验证是否成功
配置zk相关配置:
conf/zoo.cfg
#配置数据文件夹和日志文件夹
dataDir=/var/houkai/zk
dataLogDir=/var/houkai/zk/log
#配置集群 2888: leader接收writer请求用 3888:选主投票用
server.1=192.168.65.10:2888:3888
server.2=192.168.65.20:2888:3888
server.3=192.168.65.30:2888:3888
server.4=192.168.65.40:2888:3888
server.5=192.168.65.50:2888:3888
mkdir -p /var/houkai/zk
进入/var/houkai/zk文件夹
vi myid 文件中写自己的id号,即上面的.1.2.3.4.5
- 3.启动
- zkServer.sh help 查看帮助
- zkServer.sh start-foreground
- java.net.NoRouteToHostException: No route to host (Host unreachable)
- service iptables status 查看防火墙状态
- service iptables off
- service iptables stop 即时生效,重启后复原
chkconfig iptables off 永久关闭防火墙
- zkServer.sh status 查看主机状态是主还是从
- zkCli.sh 启动客户端
- o ll显示节点
- o create /newnode "" 创建新的(持久)节点,东西的话不报错但是不创建,所以带个字符串
- o create -s /newnode 创建(顺序)节点,可以规避节点被覆盖
- o create -e /newnode 创建(临时)节点
- o create [-s] [-e] path data acl 其中,-s或-e分别指定节点特性,顺序或临时节点,若不指定,则表示持久节点;acl用来进行权限控制。
- o create -s -e /newnode
- o create/newnode/sonnode "koukay" 也可以创建多级节点,数据最大1M,二进制安全的
- o get /newnode/sonnode 获取节点 因为节点相当于文件夹,所以名字前必须带/
- o get -s /koukay 获取节点,显示数据参数
- o set /newnode "hello"为节点设置值
- o rmr /test 删除节点及数据
- 参数解析
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x1469
cversion = 3
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 4
其中:
cZxid:节点创建时的zxid
ctime:节点创建时间
mZxid:节点最近一次更新时的zxid
mtime:节点最近一次更新的时间
cversion:子节点数据更新次数
dataVersion:本节点数据更新次数
aclVersion:节点ACL(授权信息)的更新次数
- ephemeral Owner:如果该节点为临时节点,ephemeralOwner值表示与该节点绑定session id.如果该节点不是临时节点,ephemeralOwner值为0
dataLength:节点数据长度,本例中为hello world的长度
numChildren:子节点个数
netstat -napt |egrep '(2888|3888)'
四,paxos,ZAB(zk原子广播),watch,API -->zk client,callback-->reactive 编程 更充分的压榨os,hw资源,性能
- zookeeper分布式协调扩展,可靠性,时序性,扩展性
- 扩展性
- o 框架架构-->角色
leader |
主节点 |
follower |
从节点 |
observer |
Observer与follower类似但不参与投票,只是简单的接收投票结果,因此我们增加再多的Observer,也不会影响集群的写性能
|
- 读写分离 observer放大查询能力
- 只有follower才能选举
- zoo.cfg
server.1=192.168.65.10:2888:3888
server.2=192.168.65.20:2888:3888
server.3=192.168.65.30:2888:3888
server.4=192.168.65.40:2888:3888
server.5=192.168.65.50:2888:3888:observer
- 可靠性
- o 攘其外必先安其内(有主有从)
- o 快速恢复leader
- o 数据可靠,可用一致性
- 攘其外 一致性?(先同步再提供服务) 最终一致性 过程中节点是否对外提供服务(zk挂了,停止服务,进行选举)
- 分布式
https://www.douban.com/note/208430424/ (关于分布式的文章,更偏向于paxos)
Z(ZK)A(原子)B(广播) 作用在可用状态 有leader时
- 分布式流程
- 概念解释:
- 原子:只有成功或失败,没有中间状态 (队列+2pc)
- 广播:分布式多节点,全部收到消息(过半)
- 队列:FIFO 顺序性
- zk的数据状态在内存用磁盘保存日志
- 一个leader两个follower ,每个follower和leader之间有个队列
- 概念解释:
client |
|
follower |
|
queue |
leader |
queue |
|
follower |
|
client |
客户端 |
1客户端请求创建节点,请求到达follower |
从节点 |
4-1-log队列将日志送达从节点 |
2从节点在主节点创建 |
3Zxid事务id |
从节点在主节点创建 |
4-1-log队列将日志送达从节点 |
从节点 |
1客户端请求创建节点,请求到达follower |
客户端 |
|
从节点返回创建状态OK |
sync可选项 |
4-1-ok 从节点收到日志后返回OK到队列 |
主节点返回OK日志 |
4-1-ok |
主节点返回OK日志 |
4-1-ok 从节点收到日志后返回OK到队列 |
sync可选项 |
从节点返回创建状态OK |
|
- 集群状态下如何选举leader
- 两个场景
- 场景一,第一次启动集群
- 场景二,leader挂了后重启集群
- 自己会有myid和Zxid 选举标准
- 经验最丰富的的Zxid
- Zxid相同就找比较大的myid
- *,过半通过的数据才是真数据,你见到的可用的Zid ,4台机器,3台通过
- 选举过程
- 3888造成两两通信
- 只要任何人投票,都会触发那个准leader发起自己的投票
- 推选制,先比较zxid,如果zxid相同,再比较myid
- 两个场景
- watch(监控,观察)
- o 统一目录结构
- o 客户端1
- o 客户端2
- o 客户端1&客户端2
- 客户端在统一目录结构创建节点,删除修改节点会有相应的事件,事件会触发回调函数将事件信息返回给另一个客户端
- 客户端1与服务端连接在统一视图目录树结构中创建一个临时节点(node: /oozz/a),建立连接,有一个session和ephemeralOwner(如果不是临时节点,该值为sessionid,如果是临时节点该值为1)
- 客户端2可以通过get /ooxx/a获取该节点,也可以通过watch /ooxx/a监控该节点
- 两个客户端之间也有通信,可以开启一个线程检测心跳
- API demo
packagecom.koukay.zookeeper;
importorg.apache.zookeeper.*;
importorg.apache.zookeeper.data.Stat;
importjava.io.IOException;
importjava.util.concurrent.CountDownLatch;
publicclassApp{
publicstaticvoidmain(String[]args)throwsException{
System.out.println("HelloWorld");
//zk是有session概念的,没有连接池的概念的
//watch观察回调分为两类
//watch的注册只发生在读类型调用,get,exist
//第一类,newzk的时候,传入的watch,这个watch是session级别的,跟path与node没有关系
CountDownLatchcd=newCountDownLatch(1);
ZooKeeperzk=newZooKeeper("192.168.65.10:2181,192.168.65.20:2181,192.168.65.30:2181,192.168.65.40:2181,192.168.65.50:2181"
,3000,
newWatcher(){
/**
*watch的回调方法
*@paramevent
*/
@Override
publicvoidprocess(WatchedEventevent){
Event.KeeperStatestate=event.getState();
Event.EventTypetype=event.getType();
Stringpath=event.getPath();
System.out.println("==========newzkwatch"+event.toString());
switch(state){
caseUnknown:
break;
caseDisconnected:
break;
caseNoSyncConnected:
break;
caseSyncConnected:
System.out.println("==================>connected");
cd.countDown();
break;
caseAuthFailed:
break;
caseConnectedReadOnly:
break;
caseSaslAuthenticated:
break;
caseExpired:
break;
caseClosed:
break;
}
switch(type){
caseNone:
break;
caseNodeCreated:
break;
caseNodeDeleted:
break;
caseNodeDataChanged:
break;
caseNodeChildrenChanged:
break;
caseDataWatchRemoved:
break;
caseChildWatchRemoved:
break;
}
}
});
cd.await();
ZooKeeper.Statesstate=zk.getState();
switch(state){
caseCONNECTING:
System.out.println("===========ing======================");
break;
caseASSOCIATING:
break;
caseCONNECTED:
System.out.println("===========ed======================");
break;
caseCONNECTEDREADONLY:
break;
caseCLOSED:
break;
caseAUTH_FAILED:
break;
caseNOT_CONNECTED:
break;
}
StringpathName=zk.create("/ooxx","olddata".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL);
Statstat=newStat();
byte[]node=zk.getData("/ooxx",newWatcher(){
@Override
publicvoidprocess(WatchedEventevent){
System.out.println("===========getDataWatch"+event.toString());
try{
//trueDefaultWatch被重新注册(newzk的那个watch)
zk.getData("/ooxx",true,stat);
}catch(KeeperExceptione){
e.printStackTrace();
}catch(InterruptedExceptione){
e.printStackTrace();
}
}
},stat);
System.out.println("=================="+newString(node));
//触发回调
Statstat1=zk.setData("/ooxx","newData".getBytes(),0);
//第二次还会触发吗
Statstat2=zk.setData("/ooxx","newData01".getBytes(),stat1.getVersion());
System.out.println("----------------asyncstart--------------");
zk.getData("/ooxx",false,newAsyncCallback.DataCallback(){
@Override
publicvoidprocessResult(intrc,Stringpath,Objectctx,byte[]data,Statstat){
System.out.println("----------------asyncover--------------");
System.out.println("----------"+ctx.toString());
System.out.println("----------"+newString(data));
}
},"123");
System.out.println("----------------asynccallback--------------");
Thread.sleep(2222222);
}
}
五,zookeeper分布式协调 配置 分布式锁
分布式配置
zookeeper配置数据data
客户端
get()
watch()
修改设置配置
触发回调
分布式锁
1,争抢锁,只有一个人能获得锁
2,获得锁的人出问题,临时节点(session)。
3,获得锁的人成功了。释放锁。
4,锁被释放、删除,别人怎么知道的?
4-1:主动轮询,心跳。。。弊端:延迟,压力
4-2:watch: 解决延迟问题。。 弊端:压力
4-3:sequence+watch:watch谁?watch前一个,最小的获得锁~!一旦,最小的释放了锁,成本:zk只给第二个发事件回调!!!!