版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qinshi965273101/article/details/82818981
1、分布式常见问题
- 容易出现死锁
- 容易活锁,处于活锁的线程都是非阻塞的,而且每个线程都抢不到资源,会造成cpu的耗费
- 集群的管理问题,比如某台的宕机需要能够检测到
- 集群配置文件的统一管理问题
- 集群中信息更新通知问题,某一台机器发布一个信息,能够让整个集群的机器都知道
- 管理集群的选举问题,管理集群的机器本身也是一个集群(例如zookeeper集群),其中有一台为主(选举得到),其他为从。
- 分布式锁
2、Zookeeper定义
Zookeeper是一个分布式的协调服务框架,Zookeeper可以解决分布式环境常见的问题:统一命名服务,信息配置管理,数据一致性,集群管理,分布式锁等等。
3、Zookeeper的安装
- 下载压缩包:链接:https://pan.baidu.com/s/1NtffnJ0ttT475KSQqAKcSw 提取码:0qp5
- 解压:tar -xf zookeeper-3.4.7.tar.gz
- 创建配置文件:zookeeper的配置文件是conf/zoo.cfg,conf目录下有一个配置文件的样本zoo_sample.cfg,复制该文件并修改文件名为zoo.cfg即可
- 更改配置文件内容:单机模式配置如下,其中dataDir为存储快照的目录,建议修改为自定义目录
# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=/opt/zookeeper/tmp
# the port at which the clients will connect
clientPort=2181
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
4、zookeeper的数据结构
Zookeeper存储数据的结构是一棵树,如图所示:
Zookeeper数据结构的特征:
- 有一个根节点 /,对于Zookeeper的操作都要基于根节点
- 每个节点都叫做Znode节点,都可以创建自己的子节点,都可以存储数据
- 整个Znode树会存储在内存中,提高查询速度。为了防止数据丢失,同时也会把Znode树落到磁盘上,磁盘路径即配置文件zoo.cfg里dataDir指定的目录
- Znode的路径是全局唯一的
- 创建节点时,需指定初始化数据,否则创建报错
- Zookeeper是事务的概念,针对每个事务(创建,更新或删除节点等),Zookeeper都会分配一个递增的Zxid(事务id)。其中有cZxid(创建节点的事务id),mZxid(修改节点的事务id)
5、Zookeeper单机模式启动,查看,关闭
- 进入bin目录,启动zookeeper:sh zkServer.sh start
- 验证是否启动:jps命令查看活动的java进程
- 查看状态:sh zkServer.sh status
- 停止服务:sh zkServer.sh stop
6、Zookeeper客户端启动及操作
小技巧:在客户端中,使用命令:ctrl + l 清屏。
- 进入bin目录,启动客户端:sh zkCli.sh
- 查询节点 ls / ls /zookeeper
- 创建节点:create /node01 hello
创建了节点 node01,并初始化数据为 “hello”
- 查看节点:get /node01
可以看出,当执行命令 create /node01 hello时,这就是一个写的事务,会分配一个事务id,事务id是一个自增的id。
- 修改节点数据:set /node01 hellodfr
- 删除节点:delete /node01 单个删除,存在子节点则删除失败 rmr /node01 递归删除,
7、zookeeper的节点类型
- create /park01 "dateStr" 普通持久节点
- create -e /park01 "dateStr" 普通临时节点
- create -s /park01 "dateStr" 顺序持久节点
- create -e -s /park01 "dateStr" 临时顺序节点
临时节点 | 客户端连接zookeeper服务并创建临时节点后,若客户端掉线,则临时节点会消失。 该特性可以用来检测集群中,有哪些机器挂掉 |
顺序节点 | 创建顺序节点后,会在节点名称后带上一个自增的顺序id 如创建节点 create -s /park01 ddff,真正创建的节点名称为 park010000000009 |
8、通过代码操作zookeeper
- 下载Zookeeper相关jar包:链接:https://pan.baidu.com/s/1ZlEBsshFOr_us44JpMGUpA 提取码:5v0x
- 创建Maven的java工程,pom.xml配置如下
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.dfr</groupId>
<artifactId>zookeeper</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.6.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.7</version>
</dependency>
</dependencies>
<build>
</build>
</project>
- jar包简单了解:工程创建成功后,可以看到依赖的jar包
netty是一个网络通信的框架,底层是基于NIO来写的,zookeeper就是通过netty来与其他机器进行网络交互的。
- Zookeeper的连接,和基本操作
package com.dfr.zookeeper;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.junit.Test;
public class TestDemo {
@Test
public void testConnect() throws Exception {
//连接操作是异步的(非阻塞连接),如果不做任何处理,可能导致还没连接成功,testConnect方法线程就已经关闭了。
//这里使用递减锁来处理
final CountDownLatch cdl = new CountDownLatch(1);
//ip端口,连接超时时间,监听者
ZooKeeper zk = new ZooKeeper("39.107.244.148:2181", 3000, new Watcher() {
@Override
public void process(WatchedEvent event) {
if(event.getState() == KeeperState.SyncConnected) {
System.out.println("连接成功!");
cdl.countDown();
}
}
});
//递减锁没减到0则阻塞
cdl.await();
//创建节点
//参数:节点,存储数据,权限,节点类型
zk.create("/gtt", "MsGao".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
//修改节点数据
//参数:节点,存储数据,数据版本号
//每更新一次,dataVersion + 1
zk.setData("/gtt", "gttTWO".getBytes(), 0);
//如果不需要确认当前数据版本,则传入版本号为-1
zk.setData("/gtt", "gtt0000".getBytes(), -1);
//删除数据
//版本号传-1,则直接删除节点
zk.delete("/gtt", -1);
//获取数据
//参数:节点,监听者,节点的状态信息如时间戳等
byte[] byteDate = zk.getData("/dfr", null, null);
System.out.println(new String(byteDate));
//获取子节点
//参数:节点,监听者
List<String> list = zk.getChildren("/", null);
for(String str : list) {
System.out.println(str);
}
}
}
- 对节点进行事件监听
事件类型
代码示例如下,注意zookeeper中监听默认是一次性的。要想永久监听需要自己处理。
package com.dfr.zookeeper;
import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.junit.Test;
public class TestDemo2 {
@Test
public void test() throws Exception {
//连接操作是异步的(非阻塞连接),如果不做任何处理,可能导致还没连接成功,testConnect方法线程就已经关闭了。
//这里使用递减锁来处理
final CountDownLatch cdl = new CountDownLatch(1);
//ip端口,连接超时时间,监听者
ZooKeeper zk = new ZooKeeper("39.107.244.148:2181", 3000, new Watcher() {
@Override
public void process(WatchedEvent event) {
if(event.getState() == KeeperState.SyncConnected) {
System.out.println("连接成功!");
cdl.countDown();
}
}
});
//对节点进行事件监听,此处以getData方法为例,可以自行选择监听事件类型
//zk的默认监听机制是:一次性监听
/*zk.getData("/dfr", new Watcher() {
@Override
public void process(WatchedEvent event) {
if(event.getType() == EventType.NodeDataChanged) {
System.out.println("节点数据改变了!");
}
}
}, null);
while(true);
*/
//永久监听
//监听事件的类型从EventType中选择
for(;;) {
final CountDownLatch countdl = new CountDownLatch(1);
zk.getData("/dfr", new Watcher() {
@Override
public void process(WatchedEvent event) {
if(event.getType() == EventType.NodeDeleted) {
System.out.println("节点数据删除了!");
countdl.countDown();
}
}
}, null);
countdl.await();
}
}
}