分布式锁
实现Watcher接口,作为以下几个应用的父类TestMainClient.java。
package com.xcy.ZK;
import org.apache.log4j.xml.DOMConfigurator;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import java.io.IOException;
public class TestMainClient implements Watcher {
protected static ZooKeeper zk = null;
protected static Integer mutex;
int sessionTimeout = 10000;
protected String root;
public TestMainClient(String connectString) {
if(zk == null){
try {
String configFile = this.getClass().getResource("/").getPath()+"org/zk/leader/election/log4j.xml";
DOMConfigurator.configure(configFile);
System.out.println("创建一个新的连接:");
zk = new ZooKeeper(connectString, sessionTimeout, this);
mutex = new Integer(-1);
} catch (IOException e) {
zk = null;
}
}
}
//synchronized:保证在同一时刻,只有一个线程可以执行某个方法或某个代码块
synchronized public void process(WatchedEvent event) {
synchronized (mutex) {
mutex.notify();
//唤醒一个等待(对象的)线程并使该线程开始执行
}
}
}
Locks.java
package com.xcy.ZK;
import org.apache.log4j.Logger;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.data.Stat;
import java.util.Arrays;
import java.util.List;
public class Locks extends TestMainClient {
public static final Logger logger = Logger.getLogger(Locks.class);
String myZnode;
public Locks(String connectString, String root) {
super(connectString);
this.root = root;
if (zk != null) {
try {
Stat s = zk.exists(root, false);
if (s == null) {
zk.create(root, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
} catch (KeeperException e) {
logger.error(e);
} catch (InterruptedException e) {
logger.error(e);
}
}
}
//查看是否拿到锁
void getLock() throws KeeperException, InterruptedException{
List<String> list = zk.getChildren(root, false);
String[] nodes = list.toArray(new String[list.size()]);
Arrays.sort(nodes);
//拿到锁
if(myZnode.equals(root+"/"+nodes[0])){
doAction();
}
else{
waitForLock(nodes[0]);
}
}
//抢锁
void check() throws InterruptedException, KeeperException {
myZnode = zk.create(root + "/lock_" , new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
getLock();
}
//线程等待,等待
void waitForLock(String lower) throws InterruptedException, KeeperException {
Stat stat = zk.exists(root + "/" + lower,true);//监控节点,如果拿到最小号节点,就从服务器通知到该线程,执行process
if(stat != null){
mutex.wait();
}
else{
getLock();
}
}
@Override
//确认已经拿到了最小节点
public void process(WatchedEvent event) {
if(event.getType() == Event.EventType.NodeDeleted){
System.out.println("得到通知");
super.process(event);
doAction();
}
}
/**
* 执行其他任务
*/
private void doAction(){
System.out.println("同步队列已经得到同步,可以开始执行后面的任务了");
}
//每个线程(客户机)都同时执行。
public static void main(String[] args) {
String connectString = "localhost:2181";
Locks lk = new Locks(connectString, "/locks");//创建父节点
try {
lk.check();
} catch (InterruptedException e) {
logger.error(e);
} catch (KeeperException e) {
logger.error(e);
}
}
}
Master选举
LeaderElection.java
package com.xcy.ZK;
import org.apache.log4j.Logger;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.data.Stat;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class LeaderElection extends TestMainClient {
public static final Logger logger = Logger.getLogger(LeaderElection.class);
public LeaderElection(String connectString, String root) {
super(connectString);
this.root = root;
if (zk != null) {
try {
Stat s = zk.exists(root, false);
if (s == null) {
zk.create(root, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
} catch (KeeperException e) {
logger.error(e);
} catch (InterruptedException e) {
logger.error(e);
}
}
}
void findLeader() throws InterruptedException, UnknownHostException, KeeperException {
byte[] leader = null;
//判断当前leader是否已被其他线程创建,且向服务器注册watch,若以后有注册就通知改线程。
try {
leader = zk.getData(root + "/leader", true, null);
} catch (KeeperException e) {
if (e instanceof KeeperException.NoNodeException) {
logger.error(e);
} else {
throw e;
}
}
//当前已被创建,直接作为follower。
if (leader != null) {
following();
} else {
String newLeader = null;
byte[] localhost = InetAddress.getLocalHost().getAddress();
//试图创建leader节点
try {
newLeader = zk.create(root + "/leader", localhost, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
} catch (KeeperException e) {
if (e instanceof KeeperException.NodeExistsException) {
logger.error(e);
} else {
throw e;
}
}
//若成功创建后,成为领导者,同时服务器通知到其他wait中的线程启动执行process;否则进入线程等待,给其他线程让位。
if (newLeader != null) {
leading();
} else {
mutex.wait();
}
}
}
@Override
//传来噩耗,被其他线程成功创建了领导者,自己只能作为follower
public void process(WatchedEvent event) {
if (event.getPath().equals(root + "/leader") && event.getType() == Event.EventType.NodeCreated) {
System.out.println("得到通知");
super.process(event);
following();
}
}
void leading() {
System.out.println("成为领导者");
}
void following() {
System.out.println("成为组成员");
}
public static void main(String[] args) {
String connectString = "localhost:2181";
LeaderElection le = new LeaderElection(connectString, "/GroupMembers");//创建父节点
try {
le.findLeader();
} catch (Exception e) {
logger.error(e);
}
}
}
队列管理
- 同步队列
Synchronizing.java
package com.xcy.ZK;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;
import org.apache.log4j.Logger;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.data.Stat;
public class Synchronizing extends TestMainClient {
int size;
String name;
public static final Logger logger = Logger.getLogger(Synchronizing.class);
/**
* 构造函数
*
* @param connectString 服务器连接
* @param root 根目录
* @param size 队列大小
*/
Synchronizing(String connectString, String root, int size) {
super(connectString);
this.root = root;
this.size = size;
if (zk != null) {
try {
Stat s = zk.exists(root, false);
if (s == null) {
zk.create(root, new byte[0], Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
}
} catch (KeeperException e) {
logger.error(e);
} catch (InterruptedException e) {
logger.error(e);
}
}
try {
name = new String(InetAddress.getLocalHost().getCanonicalHostName().toString());
} catch (UnknownHostException e) {
logger.error(e);
}
}
/**
* 加入队列
*
* @return
* @throws KeeperException
* @throws InterruptedException
*/
void addQueue() throws KeeperException, InterruptedException{
//判断当前start节点是否存在,并注册watch。
zk.exists(root + "/start",true);
//创建节点(即加入队列),临时、有序号。
zk.create(root + "/" + name, new byte[0], Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
//同时只能由一个线程执行这个代码块。
synchronized (mutex) {
List<String> list = zk.getChildren(root, false);
//若队列成员小于成员总数,线程进入等待;否则可以创建start节点,永久、无序号,同时通知等待中的线程执行process。
if (list.size() < size) {
mutex.wait();
} else {
zk.create(root + "/start", new byte[0], Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
}
}
}
@Override
//传来命令,最后一个入队列的线程创建了start节点,可以执行任务了。
public void process(WatchedEvent event) {
if(event.getPath().equals(root + "/start") && event.getType() == Event.EventType.NodeCreated){
System.out.println("得到通知");
super.process(event);
doAction();
}
}
/**
* 执行其他任务
*/
private void doAction(){
System.out.println("同步队列已经得到同步,可以开始执行后面的任务了");
}
public static void main(String args[]) {
//启动Server
String connectString = "localhost:2181";
int size = 1;
Synchronizing b = new Synchronizing(connectString, "/synchronizing", size);//创建父节点
try{
b.addQueue();
} catch (KeeperException e){
logger.error(e);
} catch (InterruptedException e){
logger.error(e);
}
}
}
- FIFO队列
FIFOQueue.java
package com.xcy.ZK;
import org.apache.log4j.Logger;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.data.Stat;
import java.nio.ByteBuffer;
import java.util.List;
public class FIFOQueue extends TestMainClient{
public static final Logger logger = Logger.getLogger(FIFOQueue.class);
/**
* Constructor
*
* @param connectString
* @param root
*/
FIFOQueue(String connectString, String root) {
super(connectString);
this.root = root;
if (zk != null) {
try {
Stat s = zk.exists(root, false);
if (s == null) {
zk.create(root, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
}
} catch (KeeperException e) {
logger.error(e);
} catch (InterruptedException e) {
logger.error(e);
}
}
}
/**
* 生产者
*
* @param i
* @return
*/
boolean produce(int i) throws KeeperException, InterruptedException{
//将i转化为字节数组
ByteBuffer b = ByteBuffer.allocate(4);
byte[] value;
b.putInt(i);
value = b.array();
zk.create(root + "/element", value, ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT_SEQUENTIAL);//生产永久、有序号的节点
return true;
}
/**
* 消费者
*
* @return
* @throws KeeperException
* @throws InterruptedException
*/
int consume() throws KeeperException, InterruptedException{
int retvalue = -1;
Stat stat = null;
//循环执行,保障被唤醒的消费者能继续消费。
while (true) {
//保证只有一个线程运行该代码块
synchronized (mutex) {
//注册watch,返回值有变化时通知线程苏醒。
List<String> list = zk.getChildren(root, true);
//若当前没有产品,线程进入等待;否则。
if (list.size() == 0) {
mutex.wait();
} else {
Integer min = new Integer(list.get(0).substring(7));//获取一个序号
//循环得到最小序号
for(String s : list){
Integer tempValue = new Integer(s.substring(7));
if(tempValue < min) min = tempValue;
}
//拿到最小序号对应的产品的字节数组
byte[] b = zk.getData(root + "/element" + min,false, stat);
//删除拿到的产品(节点)
zk.delete(root + "/element" + min, 0);
ByteBuffer buffer = ByteBuffer.wrap(b);
retvalue = buffer.getInt();//转为整型
return retvalue;
}
}
}
}
@Override
//被通知苏醒
public void process(WatchedEvent event) {
super.process(event);
}
public static void main(String args[]) {
//启动Server
TestMainServer.start();
String connectString = "localhost:"+TestMainServer.CLIENT_PORT;
FIFOQueue q = new FIFOQueue(connectString, "/app1");//创建父节点
int i;
Integer max = new Integer(5);
System.out.println("Producer");
for (i = 0; i < max; i++) {
try {
q.produce(10 + i);
} catch (KeeperException e) {
logger.error(e);
} catch (InterruptedException e) {
logger.error(e);
}
}
for (i = 0; i < max; i++) {
try{
int r = q.consume();
System.out.println("Item: " + r);
} catch (KeeperException e){
i--;
logger.error(e);
} catch (InterruptedException e){
logger.error(e);
}
}
}
}
代码来源于转载,博主仅仅加了点注释 。