[ZooKeeper之八] ZooKeeper 事务
2020-06-13 本文已影响0人
小胡_鸭
有的时候,我们希望 ZooKeeper 在执行多个操作时,要么全部成功,要么全部失败,把这多个操作看成一个原子性操作,这通常是为了解决在多线程环境中为了避免竞态条件导致的问题。
ZooKeeper 从3.4.0开始提供了multiop的特性,支持原子性地执行多个操作,基于multiop封装了事务对象 Transaction
,使用形式更加灵活方便。
一、multiop
multiop使用步骤:
(1)创建 Op
对象,通过调用 Op
类的静态方法来 create
(创建)、delete
(删除)、getData
(获取节点数据)并返回 Op
对象;
(2)将代表多个操作的多个 Op
对象以 Iterable
集合的形式传参给 ZooKeeper 的 multi
方法调用。
调用形式如下:
public List<OpResult> multi(Iterable<Op> ops) throws InterruptedException, KeeperException;
下面演示同时删除 /a
、/a/b
节点为例演示multiop的使用,操作结果只可能是全部成功或全部失败,不存在一个成功一个失败的情况,代码如下:
Op deleteZnode(String z) {
return Op.delete(z, -1);
}
// 同步版本
void testMultiop() throws InterruptedException, KeeperException {
List<OpResult> results = zk.multi(Arrays.asList(deleteZnode("/a/b"), deleteZnode("/a")));
for (OpResult result : results) {
if (result instanceof DeleteResult) {
System.out.println("delete success");
DeleteResult dresult = (DeleteResult) result;
System.out.println(dresult.hashCode());
}
}
}
假如 ZooKeeper 数据中存在 /a
、/a/b
节点,则都能删除成功。
![](https://img.haomeiwen.com/i2865141/4d64d02f26fa0e7e.png)
![](https://img.haomeiwen.com/i2865141/12ead262e8f82ec5.png)
假如只存在
/a
,执行时会抛出 NoNodeException
因为不存在 /a/b
,执行完之后发生 /a
还在。![](https://img.haomeiwen.com/i2865141/ff017a4155c92b43.png)
![](https://img.haomeiwen.com/i2865141/91eeb94166bd384b.png)
![](https://img.haomeiwen.com/i2865141/861366a9914d955f.png)
跟 ZooKeeper 的其他节点操作一样,multiop也提供了异步的版本,通过返回码判断执行结果,不用去捕获处理异常,调用形式如下:
public void multi(Iterable<Op> ops, MultiCallback cb, Object ctx);
将上面的同步代码修改成异步的形式,代码如下:
// 异步版本
void asynTestMultiop() {
zk.multi(Arrays.asList(deleteZnode("/a/b"), deleteZnode("/a")), cb, Arrays.asList(deleteZnode("/a/b"), deleteZnode("/a")));
}
MultiCallback cb = new MultiCallback() {
@Override
public void processResult(int rc, String path, Object ctx, List<OpResult> opResults) {
switch (Code.get(rc)) {
case CONNECTIONLOSS:
asynTestMultiop();
break;
case NONODE:
System.out.println("NoNode Error!");
break;
default:
System.out.println("Error when trying to delete node: " + KeeperException.create(Code.get(rc), path));
}
}
};
二、Transaction
ZooKeeper 的 Transaction
封装了multi,使用上更加灵活,在提交执行之前可以随时在原子操作中增加新的操作,还可以跨方法执行事务,commit
方法同样提供了同步和异步两种形式。
演示 Transacion
同步提交事务:
void transactionTest() throws InterruptedException, KeeperException {
Transaction t = zk.transaction();
t.delete("/a/b", -1);
t.delete("/a", -1);
List<OpResult> results = t.commit();
for (OpResult result : results) {
if (result instanceof DeleteResult) {
System.out.println("delete success");
}
}
}
演示 Transacion
跨方法异步提交事务:
void AsyncTransactionTest() {
Transaction t = zk.transaction();
t.delete("/a/b", -1);
anotherFunc(t);
}
void anotherFunc(Transaction t) {
t.delete("/a", -1);
t.commit(cb, null);
}