60.大数据之旅——电信日志项目03

版权声明:版权归零零天所有,若有转载请注明出处。 https://blog.csdn.net/qq_39188039/article/details/86635929

zookeeper概述

在这里插入图片描述
官方网址:http://zookeeper.apache.org/
What is ZooKeeper?
ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services. All of these kinds of services are used in some form or another by distributed applications. Each time they are implemented there is a lot of work that goes into fixing the bugs and race conditions that are inevitable. Because of the difficulty of implementing these kinds of services, applications initially usually skimp on them ,which make them brittle in the presence of change and difficult to manage. Even when done correctly, different implementations of these services lead to management complexity when the applications are deployed.

Zookeeper是分布式服务框架。主要是用来解决分布式应用中常见的问题,如:集群中数据的一致性、统一命名服务、集群中机器节点的状态同步服务、集群管理、分布式应用配置项的管理等。

所以如果从功能角度上来看,Zookeeper不仅仅可以应用到Hadoop生态系统中,它可以应用到任何的集群环境里,起到分布式(集群)管理和协调服务。

Zookeeper是根据Google发表的一篇论文:《The Chubby lock service for loosely-coupled distributed systems》一种锁服务:Chubby lock,旨在服务于松耦合分布式系统。

分布式思想
利用多台机器并行执行同一个任务,每台机器上都有独立的程序模块及要处理的数据,即人多干活快的思想。但是,由于由原来的单一节点运算模式转变为分布式运算,所以,代码的编写、数据资源的分配策略都发生了改变,并且也引发了很多新的问题。

分布式编程、集群管理存在的难题
死锁。在分布式系统中,产生死锁的因素一般是由于系统提供的资源数比线程进程所需要的资源数少,并且系统的资源分配策略和线程并发执行速度不当引起的(互斥使用资源+占有资源且等待资源)。
活锁,活锁最大的隐患就是,进程或线程并不是blocked的,所以会导致cpu一直在调度线程工作,最后cpu资源耗尽。
线程间由于某些条件发生碰撞,导致重新执行,如果再次尝试后依然发生碰撞,再重新执行。碰撞—再执行—碰撞……如此循环往复,就产生了活锁。

单一实体的活锁
例如线程从队列中拿出一个任务来执行,如果任务执行失败,那么将任务重新加入队列,继续执行。假设任务总是执行失败,或者某种依赖的条件总是不满足,那么线程一直在繁忙却没有任何结果。

协同导致的活锁
生活中的典型例子: 两个人在窄路相遇,同时向一个方向避让,然后又向另一个方向避让,如此反复。
通信中也有类似的例子,多个用户共享信道(最简单的例子是大家都用对讲机),同一时刻只能有一方发送信息。发送信号的用户会进行冲突检测, 如果发生冲突,就选择避让,然后再发送。 假设避让算法不合理,就导致每次发送,都冲突,避让后再发送,还是冲突。
计算机中的例子:两个线程发生了某些条件的碰撞后重新执行,那么如果再次尝试后依然发生了碰撞,长此下去就有可能发生活锁。

集群管理,如何来监控集群里某个节点的状态,比如是正常运行还是宕机了。
集群环境下的协调通知,比如集群某一个机器有数据更新或其他状态变更,需要集群里其他的机器能够及时(实时),这个需要集群管理者做到协调通知
集群环境下的任务分配。如果是手动实现,思路是:先要知道集群里有哪些节点,即集群的总数量,然后还要判断哪些节点是可用节点(是空闲还是繁忙),如果是空闲节点,就发送数据,但是集群里节点的状态是不断变化的,所以还要想办法去实时监听。
集群环境下的负载均衡,在集群任务分配过程中,可能会导致任务分配不均的情况,比如把80%的任务都分给一台机器来处理,导致这台机器的cpu工作负荷很大。所以在做分布式编程时,也需要考虑到。
配置信息的管理。要达到的效果,更改一次信息,就能够让所有的机器都更新这个信息
分布式锁的实现。比如某台抢到了一个集群里的共享资源,那其他机器就不能在用这个资源,这就相当于在分布式环境下,跨机器的环境下,实现的锁效果。针对这种锁,我们叫分布式锁。但是针对分布式锁,用sychronized,重入锁实现不了。

Zookeeper要解决的问题
在分布式应用中,由于工程师不能很好地使用锁机制,以及基于消息的协调机制不适合在某些应用中使用,因此需要有一种可靠的、可扩展的、分布式的、可配置的协调机制来统一系统的状态。Zookeeper的目的就在于此。

Zookeeper的名字
动物园管理员
数据一致性:比如多台一级引擎,配置信息都必须一样

zookeeper单机模式安装配置


安装步骤:
0.关闭虚拟机的防火墙 ,执行:service iptables stop
1.准备虚拟机,安装并配置jdk,1.6以上
配置示例:
JAVA_HOME=/home/software/jdk1.8
CLASSPATH=.: J A V A H O M E / l i b / d t . j a r : JAVA_HOME/lib/dt.jar: JAVA_HOME/lib/tools.jar
PATH= J A V A H O M E / b i n : JAVA_HOME/bin: PATH
export JAVA_HOME PATH CLASSPATH

2.上传zookeeper的安装包 3.4.7版本
3.解压安装 tar -xvf …………
4.进入zookeeper安装目录下的conf目录,有一个zoosample.cfg的文件
复制一份,并重命名为zoo.cfg文件,这个名字固定写死,因为zookeeper启动会检查这个文件,根据这个配置文件里的信息来启动服务
5.进入bin目录
执行:./zkServer.sh start 启动zookeeper
6.进入zookeeper客户端,操作zookeeper
执行:./zkCli.sh
在这里插入图片描述

Linux操作+数据模型+节点介绍


在这里插入图片描述
1.每个节点称为znode节点
2.多个znode节点共同形成了一个znode树
3.每个znode节点都可以存储数据
4.每个znode节点都具有唯一性。基于这个特性,可以通过zookeeper做统一命名服务
5.znode树是维系在内存中,供用户快速查询
6.znode节点,4种。①create /park01 ② create -e /park01 ③ create -s /park01
④ create -e -s /park01
7.临时节点的特点:当创建此临时节点的客户端断开连接后,节点别删除
8.Zxid(事务id),是一个递增的id,事务Id越大,事务越新。选举时会用到。
9.选举id,如果Zxid比较不出来,谁大谁当Leader
10.客户端连接zokeeper服务的超时的阈值:下限:2ticktime 上限20ticktime
4000~40000

zookeeperAPI操作


同步方式
一、
连接zookeeper和创建节点数据代码:

public class ZkDemo {
 
public static void main(String[] args) throws Exception {
final CountDownLatch cdl =new CountDownLatch(1);
//第一个参数,连接的zookeeper节点ip,
//第二个参数,会话超时时间,以毫秒为单位。比如设置1000*30 是30秒。如果30秒内zookeeper没有收到客户端节点的心跳,则断开连接
//第三个参数,Watcher,观察者,当zookeeper里有事件发生时,会通知到这个process方法里
//需要注意,连接是异步的,当连接成功是,会回调process方法,我们需要做相应的处理
//此外,连接一个zookeeper连接地址也可以,因为zookeep会将数据同步至其他的zookeerper节点
ZooKeeper zk=new ZooKeeper("192.168.234.137:2181",30000,new Watcher(){
 
@Override
public void process(WatchedEvent event) {
if(event.getState()==KeeperState.SyncConnected){
//说明连接成功了
cdl.countDown();
}
 
}
 
});
//之所以用递减锁,因为:连接是非阻塞的,到底连接成功没,不知道。所以,用递减锁,把这个连接变成了阻塞式连接,当成功连接后,才走
//await()之后的代码
cdl.await();
//创建节点
//1.path 节点路径;2.data[] 节点数据;3.acl 权限策略,一般设为所用用户都可以操作这个节点,并且具有所有权限,所以选:Ids.OPEN_ACL_UNSAFE;
//4.createMode 创建模式:
//PERSISTENT 持久
//PERSISTENT_SEQUENTITAL 持久顺序
//EPHEMERAL 临时
//EPHEMERAL_SEQUENTITAL 临时顺序
zk.create("/node", "hello1604Zookeeper".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
 
//也可以通过StringCallBack()回调函数来拿到创建后的节点名。rc 是返回码。0代表节点创建成功。-110代表此节点已存在,节点创建失败。
//ctx 可以是任意一个对象,这个对象也可以回调函数里接收
//需要注意的是,如果是用这种方式来创建节点,是非阻塞的
zk.create("/02","helloZK".getBytes(),Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT,new StringCallback() {
 
public void processResult(int rc, String path, Object ctx, String name) {
System.out.println(rc+":"+path+":"+ctx+":"+name);
}
}, 1);
 
}
 
 
 
}

二、
更新节点数据:

@Test
public void testSet() throws Exception{
final CountDownLatch cdl=new CountDownLatch(1);
ZooKeeper zk=new ZooKeeper("192.168.234.137:2181",30000,new Watcher(){
 
@Override
public void process(WatchedEvent event) {
if(event.getState()==KeeperState.SyncConnected){
cdl.countDown();
}
 
}
 
});
cdl.await();
//version版本号的作用:在指定路径下,有一个dataVersion,这个是数据更改的版本号,从0开始,在此节点下
//每更改一次数据,版本号递增1
//而setData(, ,version)的version的意思是:指定一个版本号,基于这个版本号来修改,但是需要注意:
//如果指定的版本号是3,而当前的dataVersion是4,即指定的版本号不存在(已过时),则会报错
//如果指定的版本号是-1,则说明无论当前dataVersion是多少,都会进行数据的更新,最常用的就是-1
zk.setData("/node", "ccc".getBytes(), -1);
}


获取节点数据

@Test
public void testGetData() throws Exception{
final CountDownLatch cdl=new CountDownLatch(1);
ZooKeeper zk=new ZooKeeper("192.168.234.137:2181",3000,new Watcher(){
 
@Override
public void process(WatchedEvent event) {
cdl.countDown();
}
});
cdl.await();
//watch,当指定节点发生监听的事件时,会触发Watcher里的方法,
//监听的事件有:1.节点数据发生变化 2.节点创建 3.节点删除 4.节点的子节点发生变化。但是监听的事件只会触发一次回调方法。
//stat,节点环境信息,一般设置为null即可。如果想要,可以传入一个空的Stat对象来接收

byte[] data=zk.getData("/node",new Watcher(){
 
@Override
public void process(WatchedEvent event) {
if(event.getType()==EventType.NodeDataChanged){
System.out.println("节点数据发生变化");
}
 
}
 
},null);
System.out.println(new String(data));
//保持线程开启
 
//getACL,得到指定路径的权限信息,权限信息被封装在返回的集合里
//此外,也可以传入一个Stat对象,用来分装节点的环境信息,比如zxid,ctime等等
Stat s1=new Stat();
List<ACL> list=zk.getACL("/02",s1);
for(ACL a:list){
System.out.println(a);
}
System.out.println(s1);
 
while(true){}
}


获取子节点

@Test
public void testGetChildren() throws Exception{
//连接代码省略
List<String> list=zk.getChildren("/", null);
for(String s:list)System.out.println(s);
}


删除节点

@Test
public void testDelete() throws Exception{
//连接代码略
zk.delete("/node", -1);
 
}


判断节点是否存在

@Test
public void testExists() throws Exception{
//如果节点不存在,stat返回值=null
//如果节点存在,stat返回节点的元数据
Stat stat=zk.exists("/node02", null);
System.out.println(stat);
 
}

异步方式

异步创建节点

@Test
public void testCreateByAsynchronous() throws Exception{
zk.create("/node", "helloworld".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT,
new StringCallback() {
 
public void processResult(int rc, String path, Object ctx, String name) {
System.out.println("节点创建成功"+rc+":"+path+":"+ctx+":"+name);
 
}
},null);
 
}
 
利用监听,判断节点是否被删除或创建事件
代码:
zk.exists("/park02",new Watcher(){
 
@Override
public void process(WatchedEvent event) {
if(event.getType()==EventType.NodeDeleted){
System.out.println("节点被删除");
}
if(event.getType()==EventType.NodeCreated){
System.out.println("节点被创建");
}
 
}
 
});

利用监听,判断子节点状态变化事件
代码:

zk.getChildren("/park01",new Watcher(){
 
@Override
public void process(WatchedEvent event) {
if(event.getType()==EventType.NodeChildrenChanged){
System.out.println("子节点发生变化");
}
 
}
 
});

API:Zookeeper实现永久监听


第一种方式代码:

public class TestZK {
@Test
public void test01() throws Exception{
final CountDownLatch cdl=new CountDownLatch(1);
ZooKeeper zk=new ZooKeeper("192.168.234.137:2181,192.168.234.138:2181,192.168.234.139:2181",30000,new Watcher(){
 
@Override
public void process(WatchedEvent event) {
System.out.println("已连接zookeeper服务");
cdl.countDown();
 
}
 
});
cdl.await();
Watcher wc=new Watcher(){
public void process(WatchedEvent event) {
if(event.getType().equals(EventType.NodeDataChanged)){
System.out.println("节点数据发生变化");
}
};
};
for(;;){
zk.getData("/engine1/engine01", wc,null);
}
}
}
 

第二种方式代码(建议使用):

@Test
public void test01() throws Exception{
final CountDownLatch cdl=new CountDownLatch(1);
ZooKeeper zk=new ZooKeeper("192.168.234.137:2181,192.168.234.138:2181,192.168.234.139:2181",30000,new Watcher(){
 
@Override
public void process(WatchedEvent event) {
System.out.println("已连接zookeeper服务");
cdl.countDown();
 
}
 
});
cdl.await();
 
for(;;){
final CountDownLatch cdll=new CountDownLatch(1);
zk.getData("/engine1/engine01", new Watcher(){
@Override
public void process(WatchedEvent event) {
if(event.getType().equals(EventType.NodeDataChanged)){
System.out.println("节点数据发生变化");
cdll.countDown();
}
 
}
},null);
cdll.await();
 
}
}

永久监听+得知更新后数据:

@Test
public void testGetData() throws Exception{
final CountDownLatch cdl=new CountDownLatch(1);
ZooKeeper zk=new ZooKeeper("192.168.234.170:2181",2000,new Watcher(){
 
@Override
public void process(WatchedEvent event) {
if(event.getState()==KeeperState.SyncConnected){
System.out.println("已成功连接服务器");
cdl.countDown();
 
}
 
}
 
});
cdl.await();
 
for(;;){
final AuthCallBack ac=new AuthCallBack();
final CountDownLatch cdl2=new CountDownLatch(1);
zk.getData("/02",new Watcher(){
 
@Override
public void process(WatchedEvent event) {
if(event.getType()==EventType.NodeDataChanged){
System.out.println("有节点数据发生变化");
System.out.println("更新后的数据"+ac.getNewData());
cdl2.countDown();
}
 
}
 
},ac,null);
cdl2.await();
}
 
}
 
class AuthCallBack implements DataCallback {
 
private String newData;
 
@Override
public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
this.newData = new String(data);
 
}
 
public String getNewData() {
return newData;
}
 
public void setNewData(String newData) {
this.newData = newData;
}
 
}

Zookeeper状态说明


状态:KeeperState.Disconnected(0)

此时客户端处于断开连接状态,和ZK集群都没有建立连接。

3.1.1事件:EventType.None(-1)

触发条件:一般是在与服务器断开连接的时候,客户端会收到这个事件。

状态:KeeperState. SyncConnected(3)

3.2.1事件:EventType.None(-1)

触发条件:客户端与服务器成功建立会话之后,会收到这个通知。

3.2.2事件:EventType. NodeCreated (1)

触发条件:所关注的节点被创建。

3.2.3事件:EventType. NodeDeleted (2)

触发条件:所关注的节点被删除。

3.2.4事件:EventType. NodeDataChanged (3)

触发条件:所关注的节点的内容有更新。注意,这个地方说的内容是指数据的版本号dataVersion。因此,即使使用相同的数据内容 来更新,还是会收到这个事件通知的。无论如何,调用了更新接口,就一定会更新dataVersion的。

3.2.5事件:EventType.NodeChildrenChanged (4)

触发条件:所关注的节点的子节点有变化。这里说的变化是指子节点的个数和组成,具体到子节点内容的变化是不会通知的。

3.3状态 KeeperState.AuthFailed(4)

acl出错

3.3.1事件:EventType.None(-1)

3.4状态 KeeperState. Expired(-112)

3.4.1事件:EventType.None(-1)

3.5状态 KeeperState.ConnectedReadOnly(5)

3.5.1事件:EventType.None(-1)

触发条件:客户端与服务器成功建立会话之后,会收到这个通知。

3.5.2事件:EventType. NodeCreated (1)

触发条件:所关注的节点被创建。

3.5.3事件:EventType. NodeDeleted (2)

触发条件:所关注的节点被删除。

3.5.4事件:EventType. NodeDataChanged (3)

触发条件:所关注的节点的内容有更新。注意,这个地方说的内容是指数据的版本号dataVersion。因此,即使使用相同的数据内容 来更新,还是会收到这个事件通知的。无论如何,调用了更新接口,就一定会更新dataVersion的。

3.5.5事件:EventType.NodeChildrenChanged (4)

触发条件:所关注的节点的子节点有变化。这里说的变化是指子节点的个数和组成,具体到子节点内容的变化是不会通知的。

3.6状态 KeeperState.SaslAuthenticated(6)

3.5.1事件:EventType.None(-1)

触发条件:建立成功或失败

KeeperException

通过zkper client访问zookeeper的每个方法都会抛出一个这个异常。通过这个异常的子异常,可以确定是什么问题。详情见KeeperException类源代码。常见有:

KeeperException.InvalidACLException :acl设置错误或无效的acl.

NodeExistsException :创建的节点已经存在。

KeeperException.NoNodeException :创建的节点不存在。一般这个异常是指在创建节点时,上级节点不存在。如创建/fruit/apple节点。/fruit不存在。

zookeeper集群安装配置


在这里插入图片描述

新:Zookeeper的事务log


事务日志指zookeeper系统在正常运行过程中,针对所有的事务操作,在返回客户端“事务成功”的响应前,zookeeper会保证已经将本次更新操作的事务日志已经写到磁盘上。
zookeeper的事务日志为二进制文件,不能通过vim等工具直接访问。其实可以通过zookeeper自带的jar包读取事务日志文件。

查看事务log执行:

java -classpath .:slf4j-api-1.6.1.jar:zookeeper-3.4.7.jar org.apache.zookeeper.server.LogFormatter ./log.000000001

查看快照文件:

java -classpath .:slf4j-api-1.6.1.jar:zookeeper-3.4.7.jar org.apache.zookeeper.server.SnapshotFormatter ./snapshot.200000000

Zookeeper权限


最近使用Zookeeper作为配置管理服务,因为配置数据有很高的安全要求,需要有权限控制,也就是需要登录才能看到Zookeeper上面的数据。

Zookeeper对权限的控制是节点级别的,而且不继承,即对父节点设置权限,其子节点不继承父节点的权限。

Zookeeper提供了几种认证方式

  • world:有个单一的ID,anyone,表示任何人。
  • auth:不使用任何ID,表示任何通过验证的用户(是通过ZK验证的用户?连接到此ZK服务器的用户?)。
  • digest:使用 用户名:密码 字符串生成MD5哈希值作为ACL标识符ID。权限的验证通过直接发送用户名密码字符串的方式完成,
  • ip:使用客户端主机ip地址作为一个ACL标识符,ACL表达式是以 addr/bits 这种格式表示的。ZK服务器会将addr的前bits位与客户端地址的前bits位来进行匹配验证权限。

digest方式比较适合我们的业务,因此采用此种方式对Zookeeper进行权限控制。

创建节点数据时:
Java代码


1.List<ACL> acls = new ArrayList<ACL>(2);     
2.  
3.Id id1 = new Id("digest", DigestAuthenticationProvider.generateDigest("admin:admin123"));  
4.ACL acl1 = new ACL(ZooDefs.Perms.ALL, id1);  
5.  
6.Id id2 = new Id("digest", DigestAuthenticationProvider.generateDigest("guest:guest123"));  
7.ACL acl2 = new ACL(ZooDefs.Perms.READ, id2);  
8.  
9.acls.add(acl1);  
10.acls.add(acl2);  
11.  
12.ZooKeeper zk = new ZooKeeper("127.0.0.1:2181", 10000, new DefaultWatcher());  
13.zk.create("/test", new byte[0], acls, CreateMode.PERSISTENT);  

登录Zookeeper读取节点数据时:
Java代码


1.ZooKeeper zk = new ZooKeeper("127.0.0.1:2181", 10000, new DefaultWatcher());  
2.zk.addAuthInfo("digest", "guest:guest123".getBytes());  
1.byte[] value = zk.getData("/test", null, new Stat());  

Linux nc安装


Linux nc 介绍和安装使用
NetCat,是一种网络工具,功能实用,可通过TCP或UDP协议传输读、写数据。同时,它还是一个网络应用Debug分析器,因为它可以根据需要创建各种不同类型的网络连接。

Linux—RPM介绍
RPM包:可以在linux环境下被安装或被卸载的程序软件包。通过Linux的 rpm指令来进行安装或卸载。
rpm常用指令:
1.rpm -qa 查看所有已安装的rpm包
2.rpm -qa | grep xxx 根据xxx关键字查找rpm的安装信息
3.rpm -ivh xxx.rpm 安装某个rpm包
4.rpm -ev --nodeps mysql-libs-5.1.71-1.el6.x86_64

Zookeeper集群指令


Zookeeper集群命令
可以通过Linux nc 工具来查看Zookeeper集群服务状态(掌握3个即可)
netcat(网络工具)

  1. 执行:echo stat|nc 127.0.0.1 2181
    查看哪个节点(想看哪个节点,就写那个节点的ip即可)被选择作为follower或者
leader
Clients:
 /127.0.0.1:40743[0](queued=0,recved=1,sent=0)
Latency min/avg/max: 0/0/0
Received: 2
Sent: 1
Connections: 1
Outstanding: 0
Zxid: 0x500000002
Mode: follower
Node count: 8
  1. 执行:echo ruok|nc 127.0.0.1 2181
    测试是否启动了该Server,若回复imok表示已经启动。

  2. 执行:echo conf | nc 127.0.0.1 2181
    输出相关服务配置的详细信息。

clientPort=2181
dataDir=/home/software/zookeeper-3.4.7/tmp/version-2
dataLogDir=/home/software/zookeeper-3.4.7/tmp/version-2
tickTime=2000
maxClientCnxns=60
minSessionTimeout=4000
maxSessionTimeout=40000
serverId=1
initLimit=10
syncLimit=5
electionAlg=3
electionPort=3888
quorumPort=2888
peerType=0
  1. echo kill | nc 127.0.0.1 2181 ,关掉server

上一篇 59.大数据之旅——电信日志项目02

猜你喜欢

转载自blog.csdn.net/qq_39188039/article/details/86635929