dubbo需要一个注册中心,而Zookeeper是我们在使用Dubbo是官方推荐的注册中心
Zookeeper的集群机制
Zookeeper是为了其他分布式程序提供服务的,所以不能随便就挂了。Zookeeper的集群机制采取的是半数存活机制
。也就是整个集群节点中有半数以上的节点存活,那么整个集群环境可用。这也是为什么说zk的集群最好是奇数个节点。
zk的作用
序号 | 功能 |
---|---|
1 | 为别的分布式程序服务的 |
2 | 本身就是一个分布式程序 |
3 | 主从协调 服务器节点动态上下线 统一配置管理 分布式共享锁 统一名称服务 |
4 | 管理(存储,读取)用户程序提交的数据 并为用户程序提供数据节点监听服务 |
Leader
Leader是Zookeeper集群工作的核心,其主要工作是:
Follower
Follower是zookeeper集群的跟随者,主要工作是:
Observer
Observer充当观察者的角色,观察Zookeeper集群的最小状态变化并将这些状态同步过来,其对于非事务请求可以独立处理
,对于事务请求,会转给Leader节点进行处理。Observer不会参与投票
,包括事务请求Proposal的投票和Leader选举投票
节点的映射关系
每个节点设置相应的ip和主机名的映射关系,方便集群环境的部署
修改hosts配置文件中的信息
配置免密登录
生成公钥和私钥
ssh-keygen
发送公钥给需要免密登录的节点
ssh-copy-id zk01
ssh-copy-id zk02
ssh-copy-id zk03
scp -r b.txt bobo01:/root/
firewall-cmd --state
停止防火墙
systemctl stop firewall.service
禁止开机启动
systemctl disable firewall.service
Leader主要作用是保证分布式数据一致性,即每个节点的存储的数据同步。
服务器初始化时Leader选举
Zookeeper由于自身的性质,一般建议选取奇数个节点进行搭建分布式服务器集群。以3个节点组成的服务器集群为例,说明服务器初始化时的选举过程。启动第一台安装Zookeeper的节点时,无法单独进行选举,启动第二台时,两节点之间进行通信,开始选举Leader。
1 变更状态。其他非Observer服务器将自己的状态改变成Looking,开始进入Leader选举。
2. 每个Server投出1张票(myid,ZXID),由于集群运行过,所以每个Server的ZXID可能不同。
假设Server1的ZXID为145,Server3的为122,第一轮投票中,Server1和Server3都投自己,
票分别为(1,145)、(3,122),将自己的票发送给集群中所有机器。
3. 每个Server接收接收来自其他Server的投票,接下来的步骤与初始化时相同。
配置Zookeeper的环境变量
为了简化我们每次操作Zookeeper而不用进入到Zookeeper的安装目录,我们可以将Zookeeper的安装信息配置到系统的环境变量中
vim /etc/profile
添加的内容
export ZOOKEPPER_HOME=/opt/zookeeper
export PATH=$PATH:$ZOOKEEPER_HOME/bin
执行source命令
source /etc/profile
我们就可以在节点的任意位置操作Zookeeper了,通过scp命令将profile文件发送到其他几个节点上
scp /etc/profile zk02:/etc/
客户端连接
通过bin目录下的zkCli.sh 命令连接即可
zkCli.sh
zkCli.sh默认连接的是当前节点的Zookeeper节点,如果我们要连接其他节点执行如下命令即可
zkCli.sh -timeout 5000 -server zk02:2181
Zookeeper的数据结构
1).znode有两种类型:
短暂性(ephemeral)(断开连接自己删除)
持久性(persistent)(断开连接不删除)
2).znode有四种形式的目录节点(默认是persistent)如下
序号 | 节点类型 | 描述 |
---|---|---|
1 | PERSISTENT | 持久节点 |
2 | PERSISTENT_SEQUENTIAL | 持久有序节点(顺序节点) |
3 | EPHEMERAL | 短暂节点 (临时节点) |
4 | EPHEMERAL_SEQUENTIAL | 短暂有序节点 (临时顺序节点) |
创建znode时设置顺序标识,znode名称后会附加一个值,顺序号是一个单调递增的计数器,有父节点维护在分布式系统中,顺序号可以被用于为所有的事件进行全局排序,这样客户端可以通过顺序号推断事件的顺序
常用命令
Zookeeper作为Dubbo的注册中心用来保存我们各个服务的节点信息,显示Zookeeper是可以实现输出的存储操作的,我们来看下Zookeeper中存储操作的基本命令
ls
ls用来查看某个节点下的子节点信息
增强的命令,查看节点下的子节点及当前节点的属性信息 ls2或者 ls -s 命令
create
创建节点信息
get
get命令用来查看节点的数据
如果要查看节点的属性信息那么我们可以通过get -s 来实现
delete
delete只能删除没有子节点的节点要删除非空节点可以通过 rmr 或者 deleteall 命令实现
set
set命令可以用来修改节点的内容。
事件监听
监听某个节点的数据内容变化,通过get命令 带 -w 参数即可,在3.4版本的Zookeeper中是通过 get path watch
来说实现监控的
然后我们在其他节点上修改app1节点的数据,会触监听事件
pom
<dependencies>
<dependency>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
<version>3.5.9version>
dependency>
<dependency>
<groupId>com.github.sgroschupfgroupId>
<artifactId>zkclientartifactId>
<version>0.1version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
dependencies>
连接ZK服务
private String connectString = "192.168.100.121:2181,192.168.122:2181,192.168.100.122:2181";
private int sessionTimeOut = 5000;
/**
* 连接Zookeeper服务端
*/
@Test
public void test1() throws IOException {
ZooKeeper zooKeeper = new ZooKeeper(connectString, sessionTimeOut, new Watcher() {
/**
* 触发监听事件的回调方法
* @param watchedEvent
*/
@Override
public void process(WatchedEvent watchedEvent) {
System.out.println("触发了.....");
}
});
System.out.println("--->" + zooKeeper);
}
/**
* 创建节点
*/
@Test
public void createNode() throws Exception{
String path = zooKeeper.create("/apptest" // 节点路径
,"HelloZookeeper".getBytes() // 节点的数据
, ZooDefs.Ids.OPEN_ACL_UNSAFE // 权限
, CreateMode.PERSISTENT // 节点类型
);
System.out.println(path);
}
/**
* 判断节点是否存在
*/
@Test
public void exist() throws Exception{
// true表示的是使用Zookeeper中的watch
Stat stat = zooKeeper.exists("/apptest", true);
if(stat != null){
System.out.println("节点存在"+ stat.getNumChildren());
}else{
System.out.println("节点不存在 ....");
}
}
/**
* 获取某个节点下面的所有的子节点
*/
@Test
public void getChildrens() throws Exception{
List<String> childrens = zooKeeper.getChildren("/app1", true);
for (String children : childrens) {
// System.out.println(children);
// 获取子节点中的数据
byte[] data = zooKeeper.getData("/app1/" + children, false, null);
System.out.println(children+":" + new String(data));
}
}
/**
* 修改节点的内容
*/
@Test
public void setData() throws Exception{
// -1 不指定版本 自动维护
Stat stat = zooKeeper.setData("/app1/a1", "666666".getBytes(), -1);
System.out.println(stat);
}
/**
* 删除节点
*/
@Test
public void deleteNode() throws Exception{
zooKeeper.delete("/app1",-1);
}
事件监听处理
/**
* 监听Node节点下的子节点的变化
*/
@Test
public void nodeChildrenChange() throws Exception{
List<String> list = zooKeeper.getChildren("/app1", new Watcher() {
/**
* None(-1),
* NodeCreated(1),
* NodeDeleted(2),
* NodeDataChanged(3),
* NodeChildrenChanged(4),
* DataWatchRemoved(5),
* ChildWatchRemoved(6);
* @param watchedEvent
*/
@Override
public void process(WatchedEvent watchedEvent) {
System.out.println("--->"+ watchedEvent.getType());
}
});
for (String s : list) {
System.out.println(s);
}
Thread.sleep(Integer.MAX_VALUE);
}
/**
* 监听节点内容变更
*/
@Test
public void nodeDataChanged() throws Exception{
byte[] data = zooKeeper.getData("/app1/a1", new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
System.out.println("--->" + watchedEvent.getType());
}
}, null);
System.out.println("--->" + new String(data));
Thread.sleep(Integer.MAX_VALUE);
}