描述:场景
分布式场景下,当用户下单时,需要对商品的库存进行操作,这是后如果在高并发情况下,可能会出现数据一致性问题,因此,就需要要求在操作时候对操作的商品进行进行锁处理,这里使用zookeeper的znode特性(znode分为临时性节点和永久性节点,在创建时可设定。临时性节点依附于客户端会话。永久性节点必须显示的由任一一个有权限的客户端进行删除。并且一个临时性的节点不应该有子节点。一个临时性节点即使绑定在客户端的会话上,对其他客户端也是可见的。)通过节点的监听(当znode改变时,watch使客户端了解相应的信息。watch由zookeeper服务的操作来设置,同时有服务的其他操作来触发。)状态来触发一些列其他操作。
思路:假设我们需要对id=1001的商品进行库存操作,此时我们可以提前在zookeeper上建立一个父节点,路径为/products,注意父节点需要是永久性节点(临时节点下不能创建子节点),然后我们可以查询/products/1001的路径是否存在,如果存在则等待,如果不存在则新建一个节点/products/1001,创建成功以后就可以对库存进行操作,操作完成后,就可以删除此节点,此时就会触发watch事件,那么等待的线程就会重新执行。
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import com.sun.xml.internal.bind.v2.TODO;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.ZooDefs.Ids;
import org.junit.Test;
import org.apache.zookeeper.ZooKeeper;
public class DemoServer {
private ZooKeeper zkCli = null;
Boolean haveLock = false;
private final String groupName = "/locks";
private String nodePath;
private static String hostname;
public void gainLockAndDoSomething() throws Exception {
zkCli = new ZooKeeper("hostName:2181", 2000, new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getType() != EventType.NodeChildrenChanged)
return;
try {
// 用zk客户端去获取锁权限
haveLock = gainLock();
if (haveLock) {
System.out.println(hostname + " gained the lock....");
// 拿到锁之后,调用业务处理方法进行业务处理
doSomeThing();
// 释放锁
releaseLock();
// 重新创建锁节点并注册监听
registerLock();
}
} catch (Exception e) {
}
}
});
// 用zk客户端向"/locks"下注册一把自己的锁节点
registerLock();
// 让线程稍微休眠一下
Thread.sleep((long) (Math.random() * 500 + 500));
// 用zk客户端去获取锁权限
haveLock = gainLock();
if (haveLock) {
System.out.println(hostname + " gained the lock....");
// 拿到锁之后,调用业务处理方法进行业务处理
doSomeThing();
// 释放锁
releaseLock();
// 重新创建锁节点并注册监听
registerLock();
}
}
private Boolean gainLock() throws KeeperException, InterruptedException {
List<String> children = zkCli.getChildren(groupName, true);
if (children.size() == 1)
return true;
Collections.sort(children);
if (children.get(0).equals(nodePath.substring(groupName.length() + 1)))
return true;
return false;
}
private void doSomeThing() throws InterruptedException {
System.out.println("begin working .......");
Thread.sleep((long) (Math.random() * 1000 + 500));
System.out.println("work has complished.....");
}
private void releaseLock() throws InterruptedException, KeeperException {
zkCli.delete(nodePath, -1);
}
private void registerLock() throws KeeperException, InterruptedException {
nodePath = zkCli.create(groupName + "/lock", null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println(nodePath);
}
@Test
public void test() throws KeeperException, InterruptedException, IOException {
zkCli = new ZooKeeper("hostName:2181", 2000, new Watcher() {
@Override
public void process(WatchedEvent event) {
// TODO Auto-generated method stub
}
});
String create = zkCli.create("/locks", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println(create);
Thread.sleep(2000);
create = zkCli.create("/locks/a", "ab".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
System.out.println(create);
byte[] data = zkCli.getData("/locks/a", true, null);
System.out.println(new String(data, "utf-8"));
}
/**
* 程序的入口
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
hostname = args[0];
// 去获取锁并且进行业务处理
DemoServer demoServer = new DemoServer();
demoServer.gainLockAndDoSomething();
// 主线程休眠
Thread.sleep(Long.MAX_VALUE);
}
}
使用Curator实现
http://blog.csdn.net/luckyzhoustar/article/details/50628419
其他分布式解决方案
http://blog.csdn.net/fansunion/article/details/52302650