java zookeeper分布式原子操作_Zookeeper--0300--java操作Zookeeper,临时节点实现分布式锁原理...

删除Zookeeper的java客户端有  :

1,Zookeeper官方提供的原生API,

2,zkClient,在原生api上进行扩展的开源java客户端

3,

一、Zookeeper原生API

1,建立连接

创建会话方法:客户端可以通过创建一个Zookeeper实例来连接zookeeper服务器

Zookeeper(arguments) 一共4个构造方法,根据参数不同

cd966c57b1230130c2f392b0ab04655f.png

connectString :连接服务器列表,用“,”逗号隔开

sessionTimeout :心跳检测时间周期(毫秒)

watcher :事件处理通知器

canBeReadOnly:标识当前会话是否支持只读

sessionId、sessionPasswd:提供连接zookeeper的sessionId和密码,通过这两个确定唯一一台客户端,目的是提供重复会话

注意:zookeeper客户端和服务器端会话的建立是一个异步的过程

代码:

packagecom.lhy.zookeeper.base;importjava.util.concurrent.CountDownLatch;importorg.apache.zookeeper.WatchedEvent;importorg.apache.zookeeper.Watcher;importorg.apache.zookeeper.Watcher.Event.EventType;importorg.apache.zookeeper.Watcher.Event.KeeperState;importorg.apache.zookeeper.ZooKeeper;/*** Zookeeper base学习笔记

*@since2015-6-13*/

public classZookeeperBase {/**zookeeper地址 ,连接集群中的一个或多个都行,多个用,隔开*/

static final String CONNECT_ADDR = "192.168.75.3:2181";/**session超时时间*/

static final int SESSION_OUTTIME = 2000;//ms

/***

* 信号量,阻塞程序执行,用于等待zookeeper连接成功,发送成功信号

*

**/

static final CountDownLatch connectedSemaphore = new CountDownLatch(1);public static void main(String[] args) throwsException{//构造器参数:1:zookeeper连接地址,2:超时时间,3:Watcher:观察者,

ZooKeeper zk = new ZooKeeper(CONNECT_ADDR, SESSION_OUTTIME, newWatcher(){

@Overridepublic voidprocess(WatchedEvent event) {//获取事件的状态

KeeperState keeperState =event.getState();

EventType eventType=event.getType();//SyncConnected表示 连接成功状态

if(KeeperState.SyncConnected ==keeperState){//刚连接成功什么事件也没有,所以是none

if(EventType.None ==eventType){//如果建立连接成功,则发送信号量,让后续阻塞程序向下执行

connectedSemaphore.countDown();

System.out.println("zk 建立连接");

}

}

}

});//主线程 进行阻塞,等待Zookeeper对象 初始化

connectedSemaphore.await();

System.out.println("连接zookeeper成功");//创建父节点//zk.create("/lhy", "niubei".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);//创建子节点//zk.create("/lhy/children", "children data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);//获取节点洗信息

byte[] data = zk.getData("/lhy", false, null);

System.out.println(newString(data));

System.out.println(zk.getChildren("/lhy", false));//修改节点的值//zk.setData("/testRoot", "modify data root".getBytes(), -1);//byte[] data = zk.getData("/testRoot", false, null);//System.out.println(new String(data));//判断节点是否存在//System.out.println(zk.exists("/testRoot/children", false));//删除节点//zk.delete("/testRoot/children", -1);//System.out.println(zk.exists("/testRoot/children", false));

zk.close();

}

}

主要是通过实例化 Zookeeper 对象进行连接的,看Zookeeper 源码:

1,connectString 表示连接地址,

2,sessionTimeout 会话超时时间

public ZooKeeper(String connectString, intsessionTimeout, Watcher watcher)throwsIOException

{this(connectString, sessionTimeout, watcher, false);

}

3, Watcher 观察者,查看其源码

英语的大概意思是,这接口是在客户端连接 Zookeeper 服务端时需要实现的接口,其中注册可很多事件,相当于连接时候的一个回调函数。这个很像swing里的监听事件

/*** This interface specifies the public interface an event handler class must

* implement. A ZooKeeper client will get various events from the ZooKeeper

* server it connects to. An application using such a client handles these

* events by registering a callback object with the client. The callback object

* is expected to be an instance of a class that implements Watcher interface.

**/

public interfaceWatcher {

......

}

需要注意的是:

a13ca44dd610d8ce8c20c722745498b7.png

实例化 Zookeeper 的过程是异步的,也就是说,在new  Zookeeper对象的同时,代码是继续往下走的,而这样就会出现问题,可能Zookeeper对象还没有初始化,下边的代码就开始调用创建节点、等操作,会出现空指针,所以用到了一个多线程的工具类,CountDownLatch ,可以让程序阻塞等待Zookeeper实例化完成代码再往下走。

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

CountDownLatch用法简单介绍

CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。

构造器中的计数值(count)实际上就是闭锁需要等待的线程数量。这个值只能被设置一次,而且CountDownLatch没有提供任何机制去重新设置这个计数值。

与CountDownLatch的第一次交互是主线程等待其他线程。主线程必须在启动其他线程后立即调用CountDownLatch.await()方法。这样主线程的操作就会在这个方法上阻塞,直到其他线程完成各自的任务。

其他N个线程必须引用闭锁对象,因为他们需要通知CountDownLatch对象,他们已经完成了各自的任务。这种通知机制是通过 CountDownLatch.countDown()方法来完成的;每调用一次这个方法,在构造函数中初始化的count值就减1。所以当N个线程都调 用了这个方法,count的值等于0,然后主线程就能通过await()方法,恢复执行自己的任务。

CountDownLatch类只提供了一个构造器:

public CountDownLatch(int count) { };//参数count为计数值

然后下面这3个方法是CountDownLatch类中最重要的方法:

1. public void await() throws InterruptedException { };//调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行

2. public boolean await( longtimeout, TimeUnit unit)throws InterruptedException { };//和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行

3. public void countDown() { };//将count值减1

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

2,操作节点

zookeeper增删改查都提供了同步和异步两套方法,来操作节点,同步的等待操作执行完响应结果,异步的方法另起线程执行。异步的方法提供了回调函数来获取一些参数。

创建:

2c8a106fa0b42eba364cd5523be22377.png

读取:

fb9c36aaba2df2ffadc9f2956067d489.png

修改:

0997ab275980ce3990498add21b4d54c.png

删除:

2b1667dd3dc49db161e6f734c05ed8b3.png

举例1,同步方式创建:

参数1,节点路径(名称): /nodeName    --(不允许递归创建节点,父节点不存在不允许创建子节点)

参数2:节点内容,要求类型是字节数组,(也就是说不支持序列化方式,如果需要实现序列化,可使用java相关序列化框架,如Hessian、Kryo框架)

参数3,节点权限,使用Ids.OPEN_ACL_UNSAFE 开放权限即可。(这个参数一般在权限没有太高要求的场景下,没必要关注)

参数4,节点类型:创建节点的类型,CreateMode.*  提供4种节点类型,临时节点本次会话有效,连接关闭就删除

PERSISTENT   (持久节点)

PERSISTENT_SEQUENTIAL(持久序列节点)

EPHEMERAL(临时节点)

EPHEMERAL_SEQUENTIAL(临时序列节点)

创建节点:不能递归创建

//创建父节点

String result = zk.create("/lhy", "niubei".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

System.err.println(result);//创建子节点

zk.create("/lhy/children", "children data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

举例2,异步方式删除

异步方式在同步方式的基础上加了两个参数

49c75554fc68a60fdabbd4a52041be56.png

参数5:注册一个异步回调函数,要实现AsyncCallback.VoidCallback 接口,重写processResult(int rc, String path, Object ctx) 方法,当节点创建完毕后执行此方法。

ee9bff78b5f3b1cd1e1df7dc9ca613c5.png

回调函数方法参数:

rc:为服务端的响应码, 0表示调用成功,-4表示端口连接,-110表示指定节点存在,-112表示会话已过期,

path:接口调用时传入API的数据节点的路径参数

ctx:为调用接口传入API的ctx值

name: 实际在服务器端创建节点的名称

参数6:传递给回调函数的参数,一般为上下文Context信息

举例3:读取

读取某个节点

d821d0bf2a2cf47107f659b8faee5639.png

读取某个节点的第一级子节点,原生api不支持递归读取

940e6890299221ae6701aab5ecb6e826.png

上述代码只能打印aaa bbb  ccc节点数据, 而不能打印aaa1

5fbfed12e4371b69086bfdba7905d58d.png

举例4:修改节点

3a09574a50bb892394f308f6212ad11f.png

举例5,节点是否存在

c80b610f6d19a2c9a813e19f30a5973d.png

会返回如下版本等信息

18e7a567bbe33f74c32b932e0bdbc1d2.png

举例6;同步删除节点

4d1e476fb4c407a2cdf0d0ce78f437f3.png

小插曲

+++++++++++++临时节点 实现分布式锁原理++++++++++++

如下图所示,场景:

应用集群部署,nginx做负载均衡,集群模拟为tomcat1,tomcat2;数据库是主备,tomcat1、2分别操作DB1,DB2,zookeeper来实现分布式锁

高并发下,多个请求(模拟2个)想要修改id=1的记录的年龄25-->26,

第一步:请求1经nginx转发到tomcat2,

第二步:tomcat2中app中zk客户端会连接zookeeper,创建/user/1 1的临时节点(主要是节点名称,值随便),

第三步:tomcat2修改DB2中uid=1的年龄25改为26,

第四步:BD2和BD1进行数据同步

第五步:同步完成,释放zookeeper连接,临时节点清除

这期间任一时刻,转发到tomcat2的请求,也想修改uid=1的年龄,tomcat2在修改前,会先get("/user/1")一 下,看Zookeeper中有没有/user/1 1的临时节点,因为zookeeper的get性能是很高的,这一步骤很快,如果zoookeeper中有/user/1 1的临时节点,就等待,因为这说明有别的线程正在操作这条数据,直到get不到/user/1 1的节点。说明别的线程都释放锁了,此时也create   /user/1 1的节点,抱着锁,进行数据写操作。

这样就能实现分布式锁了

d96fb1a5c59b1cbb27919a0c7ba1d362.png

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

欢迎关注个人公众号一起交流学习:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值