ZooKeeper分布式
1:zk的相关特性
1、一致性:数据一致性,数据按顺序分批入库。
2、原子性:事务要么都成功,要么都失败,不会局部化。
3、单一视图:客户端连接集群中的任一zk节点,数据都是一致的。
4、可靠性:每次对zk的操作状态都保存在服务器中。
5、实时性:客户端可以读取zk服务端的最新数据
2:linux上环境变量在/etc/profile 中编辑过后需要执行source /etc/profile 重新导入刚刚的配置
3:Zookeeper中zoo.cfg配置
1、tickTime:用于计算的时间单元。比如session超时:N*tickTime.
2、initLimit:用于集群,允许从节点连接并同步到 master节点的初始化连接时间,以tickTime 的倍数来表示.
3、syncLimit:用于集群, master主节点与 从节点 之间发送消息,请求和应答 时间长度(心跳机制)
4、dataDir:必须配置。
5、dataLogDir:日志目录。
6、clientPort:连接服务器的端口,默认2181
4:基本数据类型
1、是一个树型结构,类似前端开发中的tree.js组件。
2、每个节点称之为znode,它可以有子节点,也可以有数据。
3、每个节点分为临时节点和永久节点,临时节点会在客户端断开后消失。
4、每个zk节点都各自的版本号,可以通过命令行来显示节点信息。
5、每个节点数据发生变化,那么节点的版本号会累加(乐观锁)。
6、删除、修改过期节点,版本号不匹配则会报错。
6、每个zk节点存储的数据不宜过大,几K即可。
7、节点可以设置acl,可以通过权限来控制用户的访问
5:Linux的ZK客户端命令行学习:
./zkCli.sh可以启动客户端,使用help命令查看命令详解,使用Ctrl+C可以退出客户端:
zkCli.sh -server [ip]:[port] 连接zk
查看服务器状态
zkServer.sh status
启停服务器
zkServer.sh start
ZooKeeper -server host:port cmd args
stat path [watch] //stat命令用于查看节点的状态信息
set path data [version] //set命令用于设置节点的数据
ls path [watch] //ls命令用于获取路径下的节点信息,注意路径为绝对路径
delquota [-n|-b] path //delquota命令用于删除配额,-n为子节点个数,-b为节点数据长度
ls2 path [watch] //ls2命令是ls命令的增强版,比ls命令多输出本节点信息
setAcl path acl // setAcl命令用于设置节点Acl,Acl由三部分构成:1为scheme,2为user,3为permission,一般情况下表示为scheme:id:permissions
setquota -n|-b val path //setquota命令用于设置节点个数以及数据长度的配额
history //history用于列出最近的命令历史,可以和redo配合使用
redo cmdno //redo命令用于再次执行某个命令
printwatches on|off //printWatchers命令用于设置和显示监视状态,值为on或则off
delete path [version] //delete命令用于删除节点,如delete /nodeDelete
sync path //sync命令用于强制同步,由于请求在半数以上的zk server上生效就表示此请求生效,那么就会有一些zk server上的数据是旧的。sync命令就是强制同步所有的更新操作。
listquota path //查看指定znode的配额
rmr path //递归删除
get path [watch] //get命令用于获取节点的信息,注意节点的路径必须是以/开头的绝对路径。如get /
create [-s] [-e] path data acl //create命令用于创建节点,其中-s为顺序充点,-e临时节点
addauth scheme auth //addauth命令用于节点认证,使用方式:如addauth digest username:password
quit //退出客户端
getAcl path //获取节点的Acl,如getAcl /node1
close //close命令用于关闭与服务端的链接
connect host:port //连接zk服务端,与close命令配合使用可以连接或者断开zk服务端
返回信息的具体含义:
cZxid = 0x0 //节点创建时的zxid
ctime = Thu Jan 01 08:00:00 CST 1970 //节点创建时间
mZxid = 0x0 //节点最近一次更新时的zxid
mtime = Thu Jan 01 08:00:00 CST 1970 //节点最近一次更新的时间
pZxid = 0x2c //子节点的id
cversion = 10 //子节点数据更新次数
dataVersion = 0 //本节点数据更新次数
aclVersion = 0 //节点ACL(授权信息)的更新次数
ephemeralOwner = 0x0 //如果该节点为临时节点,ephemeralOwner值表示与该节点绑定的session id. 如果该节点不是临时节点,ephemeralOwner值为0
dataLength = 0 //节点数据长度,本例中为hello world的长度
numChildren = 10 //子节点个数
6:zk的作用体现:
1、master节点选举,主节点挂了以后,从节点就会接手工作,并且保证这个节点是唯一的,这也就是所谓的首脑模式,从而保证我们的集群是高可用的。
2、统一配置文件管理,即只需要部署一台服务器,则可以把相同的配置文件同步更新到其他所有服务器,此操作在云计算的特别多。
3、发布与订阅,类似消息队列MQ(amq,rmq),dubbo发布者把数据存在znode上,订阅者会读取这个数据。
4、提供分布式锁,分布式环境中不同进程之间争夺资源,类似多线程中的锁。
5、集群管理,集群中保证数据的强一致性。
7::Zookeeper-watcher机制
1、针对每个节点的操作,都会有一个监督者->wathcer。
2、当监控的某个对象(znode)发生了变化。则触发wathcer事件。
3、zk中的wathcer是一次性的,触发后立即销毁。
4、父节点、子节点 增删改查都能够触发其wathcer
5、针对不同类型的操作,触发的wathcer事件是不同的。子节点的创建事件,子节点的删除事件,子节点数据变化事件
6、ls 为父节点设置watcher,创建子节点触发:NodeChildChanged
7、ls 为父节点设置watcher,删除子节点触发:NodeChildChanged
8、ls 为父节点设置watcher,修改子节点不触发事件
get和stat的watch机制都是把当前节点作为父节点的
8:Apache Curator客户端的使用
需要依赖一下
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.0.0</version>
</dependency> <dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.0.0</version>
</dependency> <dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</dependency>
public class CuratorOperator { public CuratorFramework client = null;
public static final String zkServerPath = "192.168.1.110:2181"; /**
* 实例化zk客户端
*/
public CuratorOperator() {
/**
* 同步创建zk示例,原生api是异步的
*
* curator链接zookeeper的策略:ExponentialBackoffRetry
* baseSleepTimeMs:初始sleep的时间
* maxRetries:最大重试次数
* maxSleepMs:最大重试时间
*/
// RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 5); /**
* curator链接zookeeper的策略:RetryNTimes
* n:重试的次数
* sleepMsBetweenRetries:每次重试间隔的时间
*/
RetryPolicy retryPolicy = new RetryNTimes(3, 5000); /**
* curator链接zookeeper的策略:RetryOneTime
* sleepMsBetweenRetry:每次重试间隔的时间
*/
// RetryPolicy retryPolicy2 = new RetryOneTime(3000); /**
* 永远重试,不推荐使用
*/
// RetryPolicy retryPolicy3 = new RetryForever(retryIntervalMs) /**
* curator链接zookeeper的策略:RetryUntilElapsed
* maxElapsedTimeMs:最大重试时间
* sleepMsBetweenRetries:每次重试间隔
* 重试时间超过maxElapsedTimeMs后,就不再重试
*/
// RetryPolicy retryPolicy4 = new RetryUntilElapsed(2000, 3000); client = CuratorFrameworkFactory.builder()
.connectString(zkServerPath)
.sessionTimeoutMs(10000).retryPolicy(retryPolicy)
.namespace("workspace").build();
client.start();
} /**
*
* @Description: 关闭zk客户端连接
*/
public void closeZKClient() {
if (client != null) {
this.client.close();
}
} public static void main(String[] args) throws Exception {
// 实例化
CuratorOperator cto = new CuratorOperator();
boolean isZkCuratorStarted = cto.client.isStarted();
System.out.println("当前客户的状态:" + (isZkCuratorStarted ? "连接中" : "已关闭")); // 创建节点
String nodePath = "/super/imooc";
byte[] data = "superme".getBytes();
cto.client.create().creatingParentsIfNeeded()
.withMode(CreateMode.PERSISTENT)
.withACL(Ids.OPEN_ACL_UNSAFE)
.forPath(nodePath, data); // 更新节点数据
// byte[] newData = "batman".getBytes();
// cto.client.setData().withVersion(0).forPath(nodePath, newData); // 删除节点
// cto.client.delete()
// .guaranteed() // 如果删除失败,那么在后端还是继续会删除,直到成功
// .deletingChildrenIfNeeded() // 如果有子节点,就删除
// .withVersion(0)
// .forPath(nodePath);
不会删除workspace // 读取节点数据
// Stat stat = new Stat();
// byte[] data = cto.client.getData().storingStatIn(stat).forPath(nodePath);
// System.out.println("节点" + nodePath + "的数据为: " + new String(data));
// System.out.println("该节点的版本号为: " + stat.getVersion()); // 查询子节点
// List<String> childNodes = cto.client.getChildren()
// .forPath(nodePath);
// System.out.println("开始打印子节点:");
// for (String s : childNodes) {
// System.out.println(s);
// }
不会打印workspace // 判断节点是否存在,如果不存在则为空
// Stat statExist = cto.client.checkExists().forPath(nodePath + "/abc");
// System.out.println(statExist); // watcher 事件 当使用usingWatcher的时候,监听只会触发一次,监听完毕后就销毁
// cto.client.getData().usingWatcher(new MyCuratorWatcher()).forPath(nodePath);
// cto.client.getData().usingWatcher(new MyWatcher()).forPath(nodePath); // 为节点添加watcher
// NodeCache: 监听数据节点的变更,会触发事件
// final NodeCache nodeCache = new NodeCache(cto.client, nodePath);
// // buildInitial : 初始化的时候获取node的值并且缓存
// nodeCache.start(true);
// if (nodeCache.getCurrentData() != null) {
// System.out.println("节点初始化数据为:" + new String(nodeCache.getCurrentData().getData()));
// } else {
// System.out.println("节点初始化数据为空...");
// }
// nodeCache.getListenable().addListener(new NodeCacheListener() {
// public void nodeChanged() throws Exception {
// if (nodeCache.getCurrentData() == null) {
// System.out.println("空");
// return;
// }
// String data = new String(nodeCache.getCurrentData().getData());
// System.out.println("节点路径:" + nodeCache.getCurrentData().getPath() + "数据:" + data);
// }
// }); // 为子节点添加watcher
// PathChildrenCache: 监听数据节点的增删改,会触发事件
String childNodePathCache = nodePath;
// cacheData: 设置缓存节点的数据状态
final PathChildrenCache childrenCache = new PathChildrenCache(cto.client, childNodePathCache, true);
/**
* StartMode: 初始化方式
* POST_INITIALIZED_EVENT:异步初始化,初始化之后会触发事件
* NORMAL:异步初始化
* BUILD_INITIAL_CACHE:同步初始化
*/
childrenCache.start(StartMode.POST_INITIALIZED_EVENT); List<ChildData> childDataList = childrenCache.getCurrentData();
System.out.println("当前数据节点的子节点数据列表:");
for (ChildData cd : childDataList) {
String childData = new String(cd.getData());
System.out.println(childData);
} childrenCache.getListenable().addListener(new PathChildrenCacheListener() {
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
if(event.getType().equals(PathChildrenCacheEvent.Type.INITIALIZED)){
System.out.println("子节点初始化ok...");
} else if(event.getType().equals(PathChildrenCacheEvent.Type.CHILD_ADDED)){
String path = event.getData().getPath();
if (path.equals(ADD_PATH)) {
System.out.println("添加子节点:" + event.getData().getPath());
System.out.println("子节点数据:" + new String(event.getData().getData()));
} else if (path.equals("/super/imooc/e")) {
System.out.println("添加不正确...");
} }else if(event.getType().equals(PathChildrenCacheEvent.Type.CHILD_REMOVED)){
System.out.println("删除子节点:" + event.getData().getPath());
}else if(event.getType().equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)){
System.out.println("修改子节点路径:" + event.getData().getPath());
System.out.println("修改子节点数据:" + new String(event.getData().getData()));
}
}
}); Thread.sleep(100000); cto.closeZKClient();
boolean isZkCuratorStarted2 = cto.client.isStarted();
System.out.println("当前客户的状态:" + (isZkCuratorStarted2 ? "连接中" : "已关闭"));
} public final static String ADD_PATH = "/super/imooc/d"; }
public class MyCuratorWatcher implements CuratorWatcher { @Override
public void process(WatchedEvent event) throws Exception {
System.out.println("触发watcher,节点路径为:" + event.getPath());
} }
public class MyWatcher implements Watcher { @Override
public void process(WatchedEvent event) {
System.out.println("触发watcher,节点路径为:" + event.getPath());
}
}
9:zk设置配置文件
public class Client1 { public CuratorFramework client = null;
public static final String zkServerPath = "192.168.1.110:2181"; public Client1() {
RetryPolicy retryPolicy = new RetryNTimes(3, 5000);
client = CuratorFrameworkFactory.builder()
.connectString(zkServerPath)
.sessionTimeoutMs(10000).retryPolicy(retryPolicy)
.namespace("workspace").build();
client.start();
} public void closeZKClient() {
if (client != null) {
this.client.close();
}
} // public final static String CONFIG_NODE = "/super/imooc/redis-config";
public final static String CONFIG_NODE_PATH = "/super/imooc";
public final static String SUB_PATH = "/redis-config";
public static CountDownLatch countDown = new CountDownLatch(1); public static void main(String[] args) throws Exception {
Client1 cto = new Client1();
System.out.println("client1 启动成功..."); final PathChildrenCache childrenCache = new PathChildrenCache(cto.client, CONFIG_NODE_PATH, true);
childrenCache.start(StartMode.BUILD_INITIAL_CACHE); // 添加监听事件
childrenCache.getListenable().addListener(new PathChildrenCacheListener() {
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
// 监听节点变化
if(event.getType().equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)){
String configNodePath = event.getData().getPath();
if (configNodePath.equals(CONFIG_NODE_PATH + SUB_PATH)) {
System.out.println("监听到配置发生变化,节点路径为:" + configNodePath); // 读取节点数据
String jsonConfig = new String(event.getData().getData());
System.out.println("节点" + CONFIG_NODE_PATH + "的数据为: " + jsonConfig); // 从json转换配置
RedisConfig redisConfig = null;
if (StringUtils.isNotBlank(jsonConfig)) {
redisConfig = JsonUtils.jsonToPojo(jsonConfig, RedisConfig.class);
} // 配置不为空则进行相应操作
if (redisConfig != null) {
String type = redisConfig.getType();
String url = redisConfig.getUrl();
String remark = redisConfig.getRemark();
// 判断事件
if (type.equals("add")) {
System.out.println("监听到新增的配置,准备下载...");
// ... 连接ftp服务器,根据url找到相应的配置
Thread.sleep(500);
System.out.println("开始下载新的配置文件,下载路径为<" + url + ">");
// ... 下载配置到你指定的目录
Thread.sleep(1000);
System.out.println("下载成功,已经添加到项目中");
// ... 拷贝文件到项目目录
} else if (type.equals("update")) {
System.out.println("监听到更新的配置,准备下载...");
// ... 连接ftp服务器,根据url找到相应的配置
Thread.sleep(500);
System.out.println("开始下载配置文件,下载路径为<" + url + ">");
// ... 下载配置到你指定的目录
Thread.sleep(1000);
System.out.println("下载成功...");
System.out.println("删除项目中原配置文件...");
Thread.sleep(100);
// ... 删除原文件
System.out.println("拷贝配置文件到项目目录...");
// ... 拷贝文件到项目目录
} else if (type.equals("delete")) {
System.out.println("监听到需要删除配置");
System.out.println("删除项目中原配置文件...");
} // TODO 视情况统一重启服务
}
}
}
}
}); countDown.await(); cto.closeZKClient();
} }
public class RedisConfig { private String type; // add 新增配置 update 更新配置 delete 删除配置
private String url; // 如果是add或update,则提供下载地址
private String remark; // 备注 public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
}
RedisConfig.json相关代码
{"type":"add","url":"ftp://192.168.10.123/config/redis.xml","remark":"add"}
{"type":"update","url":"ftp://192.168.10.123/config/redis.xml","remark":"update"}
{"type":"delete","url":"","remark":"delete"}
10:zookeeper集群搭建
zk集群, 主从节点, 心跳机制(选举模式)
zookeeper集群搭建注意点:
在dataDir创建并配置数据文件 myid 内容为1/2/3 对应 server.1/2/3
通过 ./zkCli.sh -server [ip]:[port] 检测集群是否配置成功
例:
修改conf文件夹下的zoo.cfg文件加入:
server.1=192.168.56.105:2888:3888
server.2=192.168.56.105:2889:3889
server.3=192.168.56.105:2890:3890
再到dataDir里添加myid文件内容为1, 第二台集群则myid配置为2
11:ZooKeeper原生Java API的不足之处:
- 在连接zk超时的时候,不支持自动重连,需要手动操作
- Watch注册一次就会失效,需要反复注册
- 不支持递归创建节点
Apache curator:
- Apache 的开源项目
- 解决Watch注册一次就会失效的问题
- 提供的 API 更加简单易用
- 提供更多解决方案并且实现简单,例如:分布式锁
- 提供常用的ZooKeeper工具类
- 编程风格更舒服,
12
ZooKeeper分布式的更多相关文章
- Curator Zookeeper分布式锁
Curator Zookeeper分布式锁 pom.xml中添加如下配置 <!-- https://mvnrepository.com/artifact/org.apache.curator/c ...
- Zookeeper分布式集群搭建
实验条件:3台安装linux的机子,配置好Java环境. 步骤1:下载并分别解包到每台机子的/home/iHge2k目录下,附上下载地址:http://mirrors.cnnic.cn/apache/ ...
- ZooKeeper 分布式锁实现
1 场景描述 在分布式应用, 往往存在多个进程提供同一服务. 这些进程有可能在相同的机器上, 也有可能分布在不同的机器上. 如果这些进程共享了一些资源, 可能就需要分布式锁来锁定对这些资源的访问. 2 ...
- ZooKeeper分布式锁浅谈(一)
一.概述 清明节的时候写了一篇分布式锁概述,里面介绍了分布式锁实现的几种方式,其实那时候我一直沉迷于使用redis的悲观锁和乐观锁来实现分布式锁,直到一个血案的引发才让我重新认识了redis分布式锁的 ...
- [转载] zookeeper 分布式锁服务
转载自http://www.cnblogs.com/shanyou/archive/2012/09/22/2697818.html 分布式锁服务在大家的项目中或许用的不多,因为大家都把排他放在数据库那 ...
- 跟着大神学zookeeper分布式锁实现-----来自Ruthless
前几天分享了@Ruthless大神的Redis锁,发现和大家都学习了很多东西.因为分布式锁里面,最好的实现是zookeeper的分布式锁.所以在这里把实现方式和大家分享一下. zookeeper分布式 ...
- zookeeper分布式锁
摘要:分享牛原创,zookeeper使用,zookeeper锁在实际项目开发中还是很常用的,在这里我们介绍一下zookeeper分布式锁的使用,以及我们如何zookeeper分布式锁的原理.zooke ...
- zookeeper 分布式管理
分布式框架: Zookeeper与paxos算法 一. zookeeper是什么 官方说辞:Zookeeper 分布式服务框架是Apache Hadoop 的一个子项目,它主要是 ...
- 关于分布式锁原理的一些学习与思考-redis分布式锁,zookeeper分布式锁
首先分布式锁和我们平常讲到的锁原理基本一样,目的就是确保,在多个线程并发时,只有一个线程在同一刻操作这个业务或者说方法.变量. 在一个进程中,也就是一个jvm 或者说应用中,我们很容易去处理控制,在j ...
- zookeeper 分布式锁原理
zookeeper 分布式锁原理: 1 大家也许都很熟悉了多个线程或者多个进程间的共享锁的实现方式了,但是在分布式场景中我们会面临多个Server之间的锁的问题,实现的复杂度比较高.利用基于googl ...
随机推荐
- 移动端Hybird的网络层优化策略
一.前端代码策略:域名切换(多域名部署),解决DNS缓存及域名劫持 二.客户端策略 客户端在空闲时ping cdn节点域名列表中的域名,测量延时.丢包等数据.如果延迟 > xxx,丢包 > ...
- MongoDB 创建集合
createCollection() 方法 MongoDB db.createCollection(name, options) 是用来创建集合. 语法: 基本的 createCollection() ...
- C++类继承--基类析构函数加上Virtual
下面的内容要说明两个问题:1. 基类的析构函数为什么要加上Virtual--防止内存泄露 1. 基类虚构函数无virtual,派生类无法析构,会导致内存泄露 #include <stdio.h& ...
- Python爬虫教程-17-ajax爬取实例(豆瓣电影)
Python爬虫教程-17-ajax爬取实例(豆瓣电影) ajax: 简单的说,就是一段js代码,通过这段代码,可以让页面发送异步的请求,或者向服务器发送一个东西,即和服务器进行交互 对于ajax: ...
- 微信小程序开发4-JSON
1.JSON是JavaScript语法的子集 2.JSON的语法规则 数据在名称/值对中 数据由逗号分隔 大括号保存对象 中括号保存数组 3.JSON 值可以是: 数字(整数或浮点数) 字符串(在双引 ...
- Python学习系列----第三章 控制流
在python中有三种控制流语句,分别是: if.for.while. 2.1 if 语句 if 语句用来检验一个条件,如果条件为真,我们运行一块语句(称为 if-块),否 则我们处理另外一块语句(称 ...
- LeetCode-Maximal Rectangle[code]
code: #include <iostream> #include <vector> #include <stack> #include <algorith ...
- tempdb过大事故记录-sqlserver
今天收到预警消息,提示磁盘空间已经满了,感觉很奇怪.刚装的新机器怎么可能会磁盘空间不足.登陆看了看 可以看的到tempdb已经65G的了,而且显示是百分百可用.这个就很奇怪了,为什么会出现这种情况呢. ...
- VS断点不生效
工程属性页中“配置属性”->“C/C++”->“常规”->“调试信息格式”,选择“用于“编辑并继承”的程序数据库(/ZI)”. 在“配置属性”->“链接器”->“调试”- ...
- VS配置Qt
https://blog.csdn.net/qq_35488967/article/details/71516640