以下是linux环境的安装和使用
先要安装java环境
然后再http://zookeeper.apache.org/releases.html 这里下载zookeeper
使用解压命令
tar -zxf zookeeper-3.4.6.tar.gz
然后创建一个存放zookeeper数据的目录,在conf目录下创建zoo.cfg文件,并将数据存放目录配置在里面
zoo.cfg配置如下:
tickTime = 2000
dataDir = /*数据存放目录*/
clientPort = 2181
initLimit = 5
syncLimit = 2
bin/zkServer.sh start
启动客户端
bin/zkCli.sh
创建Znode
create /path value
/path是Znode路径 value是初始化值
更新Znode值
set /path value
移除Znode
rmr /path
删除Znode
delete /path
delete如果有子节点则不会删除成功,rmr有子节点会递归删除
创建子节点
create /path/path value
列出Znode的子节点
ls /path
得到子节点数组
检查状态
stat /path
pom依赖
org.apache.zookeeper
zookeeper
3.4.6
org.slf4j
slf4j-api
1.6.1
connection代码
public class ZookeeperConnection {
private static CountDownLatch connectedSemaphore = new CountDownLatch(1);
private ZooKeeper zk;
public ZooKeeper connect(String host) throws IOException, InterruptedException{
zk = new ZooKeeper(host, 5000, new Watcher() {
@Override
public void process(WatchedEvent event) {
if(KeeperState.SyncConnected.equals(event.getState())){
connectedSemaphore.countDown();
// synchronized (ZookeeperConnection.class) {
// ZookeeperConnection.class.notify();
// }
}
}
});
// synchronized (ZookeeperConnection.class) {
// ZookeeperConnection.class.wait();
// }
connectedSemaphore.await();
return zk;
}
public void close() throws InterruptedException {
zk.close();
}
}
查到的资料上都是用的CountDownLatch,我试了一下在只创建一个连接的情况下也可以使用同步代码块+wait()/notify()进行操作
操作zookeeper代码
import java.io.IOException;
import java.util.List;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
public class ZookeeperTemplate {
private ZooKeeper zk;
private static ZookeeperConnection conn = new ZookeeperConnection();
public ZookeeperTemplate(String host) throws IOException, InterruptedException {
super();
init(host);
}
private void init(String host) throws IOException, InterruptedException {
zk = conn.connect(host);
}
//创建Znode
public void create(String path, byte[] data) throws KeeperException,InterruptedException {
zk.create(path,// Znode路径
data, // 存储在指定znode路径中的数据
ZooDefs.Ids.OPEN_ACL_UNSAFE,// 节点的访问控制列表
CreateMode.PERSISTENT //节点的类型,即临时,顺序或两者
);
}
//检查Znode是否存在 返回Znode元数据
public Stat exists(String path) throws KeeperException, InterruptedException{
return zk.exists(path, true);
}
//获取Znode中存储的数据
public byte[] getData(String path) throws KeeperException, InterruptedException{
return zk.getData(path, new Watcher() {//当znode的数据改变时,ZooKeeper进行通知,并且只通知一次
@Override
public void process(WatchedEvent event) {
//EventType.NodeCreated : 节点创建
//EventType.NodeDeleted : 节点被删除
//EventType.NodeDataChanged : 节点被修改
//EventType.None : 客户端与服务器成功建立会话
//EventType.NodeChildrenChanged : 子节点列表发生变更
if (!(event.getType() == Event.EventType.None)){
//TODO 可以记录日志或者做其他的来应对Znode改变的情况
System.out.println("dataChanged");
}
}
}, (Stat)null //用于接收Znode元数据
);
}
//更新Znode中的数据
public void update(String path, byte[] data) throws KeeperException,InterruptedException {
zk.setData(path,// Znode路径
data, // 存储在指定znode路径中的数据
exists(path).getVersion() // znode的当前版本。每当数据更改时,ZooKeeper会更新znode的版本号
);
}
//获取某个Znode的子节点
public List getChildren(String path) throws KeeperException,InterruptedException {
return zk.getChildren(path,false);
}
//关闭连接
public void close() throws InterruptedException{
conn.close();
}
}
pom依赖
org.apache.curator
curator-framework
2.12.0
org.apache.curator
curator-recipes
2.12.0
基本用法已列在下面的这个测试类中
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.BackgroundCallback;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
/*
* 使用Curator操作zookeeper
*/
public class CuratorUsage {
public static void main(String[] args) throws Exception {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);//重试策略,默认有四种重试策略(也可以自己实现RetryPolicy接口) 这里是每隔1秒重试一次,最多重试3次
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("172.16.3.201:2181")//zookeeper的服务列表主机:端口,多个zookeeper用逗号隔开
.sessionTimeoutMs(5000)//会话超时时间,单位毫秒,默认60000ms
.connectionTimeoutMs(5000)//连接创建超时时间,单位毫秒,默认60000ms
.retryPolicy(retryPolicy)
.namespace("FirstZnode")//指定命名空间之后都是在改Znode下进行操作
.build();
client.start();
//创建Znode
client.create()
.creatingParentContainersIfNeeded()//递归创建父节点 没有这个的话如果父节点不存在会报错NoNodeException
.withMode(CreateMode.PERSISTENT)// 持久化模式 --PERSISTENT:持久化 PERSISTENT_SEQUENTIAL:持久化并且带序列号 EPHEMERAL:临时 EPHEMERAL_SEQUENTIAL:临时并且带序列号
.forPath("/path1","iiiii".getBytes());//节点路径和初始化内容
//删除Znode
client.delete()
.guaranteed()//在会话过程中,在后台持续进行删除操作,直到删除节点成功。
.deletingChildrenIfNeeded()//递归删除子节点
.withVersion(1)//指定版本
.forPath("/path1");//要删除的Znode
//获取Znode元数据
Stat stat = new Stat();
client.getData().storingStatIn(stat).forPath("/path1");
//更新Znode
client.setData()
.withVersion(1)//指定版本
.forPath("/path1","new data".getBytes());//znode路径和新的数据
//检查节点是否存在
Stat stat2 = client.checkExists().forPath("path1");
//获取子节点
List list = client.getChildren().forPath("path");
//事务操作 每个forPath之间用and()最后commit()组成原子性操作
client.inTransaction().check().forPath("/path2")
.and()
.create().withMode(CreateMode.PERSISTENT).forPath("/path2","data1".getBytes())
.and()
.setData().withVersion(1).forPath("/path2","data2".getBytes())
.and()
.commit();
//异步操作 通过回调函数进行后续处理
Executor executor = Executors.newFixedThreadPool(2);
client.create()
.creatingParentsIfNeeded()
.withMode(CreateMode.EPHEMERAL)
.inBackground(new BackgroundCallback() {
@Override
public void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent)
throws Exception {
System.out.println(client.equals(curatorFramework));//结果为true 这个curatorFramework就是执行create()操作的client
//异步拿到结果
//curatorEvent.getType() 事件类型
//curatorEvent.getResultCode() 响应码 0:调用成功 -4:断开连接 -110:节点已存在 -112:会话过期
System.out.println("eventType:"+curatorEvent.getType()+",resultCode:"+curatorEvent.getResultCode());
}
},executor)//不指定executor会使用默认的EventThread
.forPath("/path3");
}
}
a.缓存监听(实时监听zookeeper并缓存到本地)
有三种缓存监听分别是PathChildrenCache,NodeCache和TreeCache,其中PathChildrenCache监听并缓存子节点,NodeCache监听并缓存当前节点,TreeCache监听并缓存整个树节点
下面以PathChildrenCache为例:
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.curator.framework.state.ConnectionState;
import org.apache.curator.framework.state.ConnectionStateListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
public class PathChildrenCacheUsage {
public static void main(String[] args) throws Exception {
//创建client
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("172.16.3.201:2181")
.sessionTimeoutMs(5000)
.connectionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
// .namespace("FirstZnode")
.build();
//创建连接状态监听
ConnectionStateListener connectionStateListener = new ConnectionStateListener() {
@Override
public void stateChanged(CuratorFramework client, ConnectionState newState) {
// TODO Auto-generated method stub
}
};
//注册连接状态监听
client.getConnectionStateListenable().addListener(connectionStateListener);
client.start();
//使用client对/FirstZnode进行监听 第三个参数为true的话节点信息就会被放入stat
PathChildrenCache cache = new PathChildrenCache(client, "/FirstZnode", true);
//创建缓存监听
PathChildrenCacheListener cacheListener = new PathChildrenCacheListener() {
//第一次获取的时候,有几个节点就会触发几次事件
@Override
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event)
throws Exception {
System.out.println("事件类型:" + event.getType());
//如果new PathChildrenCache(client, "/FirstZnode", true);这个第三个参数填的是fasle name就不会缓存数据
if (null != event.getData()) {
//event.getData()是获取发生事件的节点 类型ChildData 如果/FirstZnode下有一个节点/path1 在给/FirstZnode增加一个子节点/path2时,path2会触发事件
System.out.println("节点数据:" + event.getData().getPath() + ":" + new String(event.getData().getData(), "UTF-8"));
}
}
};
//注册缓存监听
cache.getListenable().addListener(cacheListener);
//启动cache
cache.start();
client.create().creatingParentsIfNeeded().forPath("/FirstZnode/aaa", "01".getBytes());
Thread.sleep(100);
//PathChildrenCacheListener值监听这个节点下的子节点,不监听子节点的子节点
client.create().creatingParentsIfNeeded().forPath("/FirstZnode/aaa/bbb", "02".getBytes());
Thread.sleep(100);
//PathChildrenCacheListener值监听这个节点下的子节点,不监听该节点
client.setData().forPath("/FirstZnode","mmm".getBytes());
Thread.sleep(100);
//获取所有子节点 如果没有触发监听事件的话即便有子节点也不会获取到,因为是在触发监听的时候将节点信息缓存的
System.out.println(cache.getCurrentData());
for (ChildData data : cache.getCurrentData()) {
System.out.println("getCurrentData:" + data.getPath() + ":" + new String(data.getData(), "UTF-8"));
}
//关闭cache
cache.close();
//关闭客户端
client.close();
}
}
b.leader选举
leader选举是多个客户端操作同一个zookeeper时,多个客户端之间进行选举出一个leader作为协调者,leader将任务进行分配给其他的follower
这里的leader选举不是zookeeper集群的leader选举,这里的leader是客户端进程的leader,主要是为了解决多个进程添加同一个Znode保证只有一个进程能添加成功。
在有客户端被加入选举的队列时,选举开始。在leader挂了之后选举队列中的客户端实例也会进行leader选举。客户端的leader选举有两种方式:LeaderLatch和LeaderSelector。
LeaderLatch:
import java.util.List;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.imps.CuratorFrameworkState;
import org.apache.curator.framework.recipes.leader.LeaderLatch;
import org.apache.curator.framework.recipes.leader.LeaderLatchListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.shaded.com.google.common.collect.Lists;
import org.apache.curator.utils.CloseableUtils;
public class LeaderLatchUsage {
public static void main(String[] args) throws Exception {
List clients = Lists.newArrayList();
List examples = Lists.newArrayList();
try {
for (int i = 0; i < 10; i++) {
//创建多个操作同一个client的客户端,这些同时创建的客户端会进行选举出一个leader作为协调者,将任务分配给每个follower
//创建client
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("172.16.3.201:2181")
.sessionTimeoutMs(5000)
.connectionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();
clients.add(client);
LeaderLatch latch = new LeaderLatch(client,
"/FirstZnode", //节点
"client" + i);//LeaderLatch的id
latch.addListener(new LeaderLatchListener() {
@Override
public void isLeader() {
System.out.println("Leader");
}
@Override
public void notLeader() {
System.out.println("notLeader");
}
});
examples.add(latch);
client.start();
//将这个实例添加到leader选举并尝试获得leadership(异步)
latch.start();
}
Thread.sleep(5000);
LeaderLatch currentLeader = null;
for (LeaderLatch latch : examples) {
if (latch.hasLeadership()) {
currentLeader = latch;
}
}
System.out.println("current leader is " + currentLeader.getId());
//将当前leader关闭
System.out.println("关闭leader");
currentLeader.close();
Thread.sleep(1000);
for (LeaderLatch latch1 : examples) {
if (latch1.hasLeadership()) {
currentLeader = latch1;
}
}
System.out.println("current leader is " + currentLeader.getId());
//加入一个新的客户端到选举队列
System.out.println("增加客户端");
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("172.16.3.201:2181")
.sessionTimeoutMs(5000)
.connectionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();
clients.add(client);
LeaderLatch latch = new LeaderLatch(client,
"/FirstZnode",
"client10");
latch.addListener(new LeaderLatchListener() {
@Override
public void isLeader() {
System.out.println("Leader");
}
@Override
public void notLeader() {
System.out.println("notLeader");
}
});
examples.add(latch);
client.start();
latch.start();
for (LeaderLatch latch1 : examples) {
if (latch1.hasLeadership()) {
currentLeader = latch1;
}
}
System.out.println("current leader is " + currentLeader.getId());
} finally {
for (LeaderLatch latch : examples) {
if (null != latch.getState() && LeaderLatch.State.CLOSED != latch.getState()){
CloseableUtils.closeQuietly(latch);
}
}
for (CuratorFramework client : clients) {
if(client != null && CuratorFrameworkState.STOPPED != client.getState()){
CloseableUtils.closeQuietly(client);
}
}
}
}
}
LeaderSelector:
import java.io.Closeable;
import java.io.IOException;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.leader.LeaderSelector;
import org.apache.curator.framework.recipes.leader.LeaderSelectorListenerAdapter;
public class LeaderSelectorAdapter extends LeaderSelectorListenerAdapter implements Closeable{
private final String name;
private final LeaderSelector leaderSelector;
public LeaderSelectorAdapter(CuratorFramework client, String path, String name) {
this.name = name;
leaderSelector = new LeaderSelector(client, path, this);
leaderSelector.autoRequeue();
}
public void start() throws IOException {
leaderSelector.start();
}
@Override
public void close() throws IOException {
leaderSelector.close();
}
@Override
//当一个client进程成为leader时,takeLeadership方法被调用,当takeLeadership方法结束时重新选举新的leader
public void takeLeadership(CuratorFramework client) throws Exception {
System.out.println(name + " is now the leader.");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
import java.io.IOException;
import java.util.List;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.shaded.com.google.common.collect.Lists;
import org.apache.curator.utils.CloseableUtils;
public class LeaderSelectorUsage {
public static void main(String[] args) throws IOException, InterruptedException {
List clients = Lists.newArrayList();
List examples = Lists.newArrayList();
try {
for (int i = 0; i < 10; i++) {
CuratorFramework client
= CuratorFrameworkFactory.newClient("172.16.3.201:2181", new ExponentialBackoffRetry(20000, 3));
clients.add(client);
LeaderSelectorAdapter selectorAdapter = new LeaderSelectorAdapter(client, "/FirstZnode", "client" + i);
examples.add(selectorAdapter);
client.start();
selectorAdapter.start();
}
Thread.sleep(50000);
} finally {
for (LeaderSelectorAdapter example : examples) {
CloseableUtils.closeQuietly(example);
}
for (CuratorFramework client : clients) {
CloseableUtils.closeQuietly(client);
}
}
}
}
c.分布式锁(类似很多jdk的锁,但支持分布式的情况)
InterProcessMutex分布式可重入锁(类似jdk中的ReentrantLock)
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;
public class InterProcessMutexUsage {
public static void main(String[] args) throws Exception {
new Thread() {
@Override
public void run() {
//对同一个Znode有效
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("172.16.3.201:2181")
.sessionTimeoutMs(5000)
.connectionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();
client.start();
InterProcessMutex mutex = new InterProcessMutex(client, "/FirstZnode");
try {
mutex.acquire();
System.out.println("run方法线程获取锁");
Thread.sleep(3000);
System.out.println("run方法线程释放锁");
mutex.release();
} catch (Exception e) {
e.printStackTrace();
} finally{
client.close();
}
}
}.start();
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("172.16.3.201:2181")
.sessionTimeoutMs(5000)
.connectionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();
client.start();
InterProcessMutex mutex = new InterProcessMutex(client, "/FirstZnode");
//尝试获取锁
mutex.acquire();
System.out.println("main方法线程获取锁");
Thread.sleep(3000);
System.out.println("main方法线程释放锁");
mutex.release();
client.close();
}
}
InterProcessMutex可以通过makeRevocable方法设置监听,监听是否有其他线程请求获取锁,并协商出让锁。想要获取锁的线程调用Revoker类的attemptRevoke静态方法。在makeRevocable调用mutex.release()会报错"You do not own the lock:",因为只有acquire的那个线程可以release,所以这里使用CountDownLatch。
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.framework.recipes.locks.RevocationListener;
import org.apache.curator.framework.recipes.locks.Revoker;
import org.apache.curator.retry.ExponentialBackoffRetry;
public class InterProcessMutexUsage {
public static void main(String[] args) throws Exception {
CountDownLatch latch = new CountDownLatch(1);
new Thread() {
@Override
public void run() {
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("172.16.3.201:2181")
.sessionTimeoutMs(5000)
.connectionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();
client.start();
//锁对同一个Znode有效
InterProcessMutex mutex = new InterProcessMutex(client, "/FirstZnode");
try {
//为了让main线程先执行
Thread.sleep(100);
//通知拿到锁的线程
Revoker.attemptRevoke(client, mutex.getParticipantNodes().iterator().next());
System.out.println("sdddfff");
//尝试获取锁
mutex.acquire();
System.out.println("run方法线程获取锁");
Thread.sleep(3000);
System.out.println("run方法线程释放锁");
mutex.release();
} catch (Exception e) {
e.printStackTrace();
} finally{
client.close();
}
}
}.start();
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("172.16.3.201:2181")
.sessionTimeoutMs(5000)
.connectionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();
client.start();
InterProcessMutex mutex = new InterProcessMutex(client, "/FirstZnode");
mutex.makeRevocable(new RevocationListener() {
@Override
public void revocationRequested(InterProcessMutex forLock) {
if(mutex.isAcquiredInThisProcess()){
System.out.println("main方法线程收到请求,并释放锁");
latch.countDown();
}
}
});
//尝试获取锁
mutex.acquire();
System.out.println("main方法线程获取锁");
latch.await(5000,TimeUnit.MILLISECONDS);
if(mutex.isAcquiredInThisProcess()){
System.out.println("main方法线程释放锁");
mutex.release();
}
client.close();
}
}
不可重入锁InterProcessSemaphoreMutex
InterProcessSemaphoreMutex没有makeRevocable方法,不能进行协商出让锁。其他与InterProcessMutex类似。
读写锁InterProcessReadWriteLock(类似jdk的ReadWriteLock)
信号量InterProcessSemaphoreV2 (类似jdk的Semaphore)
组合锁InterProcessMultiLock
d.分布式计数器
int计数器SharedCount
import java.util.Random;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.shared.SharedCount;
import org.apache.curator.framework.recipes.shared.SharedCountListener;
import org.apache.curator.framework.recipes.shared.SharedCountReader;
import org.apache.curator.framework.state.ConnectionState;
import org.apache.curator.retry.ExponentialBackoffRetry;
public class SharedCountUsage {
public static void main(String[] args) throws Exception {
Random random = new Random();
for(int i = 0; i < 5; i++){
String c ="client"+i;
new Thread(new Runnable() {
@Override
public void run() {
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("172.16.3.201:2181")
.sessionTimeoutMs(5000)
.connectionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();
client.start();
//定义计数器,如果该节点不存在就创建并设置初值为0
SharedCount sharedCount = new SharedCount(client, "/FirstZnode/count", 0);
try {
//计数器启动
sharedCount.start();
int a = random.nextInt(10);
System.out.println(c + "trying add count :"+a);
//尝试设置值,如果version与预期不一样则会失败
boolean boo = sharedCount.trySetCount(sharedCount.getVersionedValue(),sharedCount.getCount()+a);
System.out.println(c+" is success : " + boo);
sharedCount.close();
client.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("172.16.3.201:2181")
.sessionTimeoutMs(5000)
.connectionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();
client.start();
SharedCount sharedCount = new SharedCount(client, "/FirstZnode/count", 0);
//给计数器设置监听
sharedCount.addListener(new SharedCountListener() {
@Override
public void stateChanged(CuratorFramework client, ConnectionState newState) {
}
@Override
public void countHasChanged(SharedCountReader sharedCount, int newCount)
throws Exception {
System.out.println("检测到count变化,newcount:"+newCount);
}
});
sharedCount.start();
Thread.sleep(2000);
//获取计数器的值
System.out.println(sharedCount.getCount());
sharedCount.close();
client.close();
}
}
}
long计数器DistributedAtomicValue
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.atomic.AtomicValue;
import org.apache.curator.framework.recipes.atomic.DistributedAtomicLong;
import org.apache.curator.retry.ExponentialBackoffRetry;
public class DistributedAtomicValueUsage {
public static void main(String[] args) throws InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(10);
for (int i = 0; i < 5; i++) {
FutureTask task = new FutureTask(new Callable() {
@Override
public Long call() throws Exception {
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("172.16.3.201:2181")
.sessionTimeoutMs(5000)
.connectionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();
client.start();
DistributedAtomicLong count = new DistributedAtomicLong(client, "/FirstZnode/longcount", new ExponentialBackoffRetry(10, 3));
//计数器加一
AtomicValue value = count.increment();
System.out.println("succeed:" + value.succeeded());
if(value.succeeded()){
System.out.println("prevalue:" + value.preValue() + ";postvalue" + value.postValue());
}
client.close();
return value.postValue();
}
});
service.submit(task);//获取返回值task.get();
}
Thread.sleep(10000);
service.shutdown();
}
}
e.分布式队列
建议还是使用其他MQ,不要使用zookeeper作为队列,因为zookeeper节点过多会影响使用性能,而消息队列经常会有成千上万条消息
zookeeper的队列使用以下几个类,在此不做过多介绍。
DistributedQueue、DistributedIdQueue、DistributedPriorityQueue、DistributedDelayQueue、SimpleDistributedQueue
f.分布式栅栏
单栅栏DistributedBarrier(类似jdk中的CyclicBarrier)
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.barriers.DistributedBarrier;
import org.apache.curator.retry.ExponentialBackoffRetry;
public class DistributedBarrierUsage {
public static void main(String[] args) throws Exception {
ExecutorService service = Executors.newFixedThreadPool(10);
for (int i = 0; i < 5; i++) {
FutureTask task = new FutureTask(new Callable() {
@Override
public String call() throws Exception {
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("172.16.3.201:2181")
.sessionTimeoutMs(5000)
.connectionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();
client.start();
//定义栅栏
DistributedBarrier barrier = new DistributedBarrier(client, "/FirstZnode/barrier");
System.out.println("设置栅栏前进行的工作");
//设置栅栏
barrier.setBarrier();
//在栅栏前等待,当栅栏移除后所有线程同时继续
barrier.waitOnBarrier();
System.out.println("设置栅栏后进行的工作");
client.close();
return "";
}
});
service.submit(task);
}
Thread.sleep(1000);
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("172.16.3.201:2181")
.sessionTimeoutMs(5000)
.connectionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();
client.start();
DistributedBarrier barrier = new DistributedBarrier(client, "/FirstZnode/barrier");
//移除栅栏
barrier.removeBarrier();
client.close();
Thread.sleep(2000);
service.shutdown();
}
}
双栅栏DistributedDoubleBarrier
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.barriers.DistributedDoubleBarrier;
import org.apache.curator.retry.ExponentialBackoffRetry;
public class DistributedDoubleBarrierUsage {
public static void main(String[] args) throws InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(10);
for (int i = 0; i < 5; i++) {
service.submit(new Runnable() {
@Override
public void run() {
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("172.16.3.201:2181")
.sessionTimeoutMs(5000)
.connectionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();
client.start();
//定义栅栏,栅栏中的成员数量为5 这个数值是一个阈值 不是限制 也就是可以允许超过这个数量的成员进入栅栏 这个值设置了是当有五个成员调用了enter方法后,这5个会同时继续执行
DistributedDoubleBarrier barrier = new DistributedDoubleBarrier(client, "/FirstZnode/doublebarrier", 5);
try {
System.out.println("enter前进入等待");
barrier.enter();
System.out.println("enter后同时执行");
barrier.leave();
Thread.sleep(1000);
//可重复enter
System.out.println("再次enter");
barrier.enter();
System.out.println("继续执行");
barrier.leave();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
Thread.sleep(5000);
service.shutdown();
}
}
服务注册中心示例代码:
import java.io.IOException;
import java.util.Map;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.imps.CuratorFrameworkState;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.TreeCache;
import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
import org.apache.curator.framework.recipes.cache.TreeCacheListener;
import org.apache.curator.framework.recipes.leader.LeaderLatch;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.springframework.stereotype.Component;
@Component
public class ServiceCenter {
private CuratorFramework client;
private String connectString = "172.16.3.201:2181";
private LeaderLatch latch;
private TreeCache cache;
public ServiceCenter(String connectString) {
super();
if(connectString != null && (!"".equals(connectString))){
this.connectString = connectString;
}
try {
init();
} catch (Exception e) {
e.printStackTrace();
}
}
//服务注册中心初始化
private void init() throws Exception {
client = CuratorFrameworkFactory.builder()
.connectString(connectString)
.sessionTimeoutMs(5000)
.connectionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();
client.start();
latch = new LeaderLatch(client, "/Services");
latch.start();
if(client.checkExists().forPath("/Services") == null){
client.create().withMode(CreateMode.PERSISTENT).forPath("/services");
}
cache = new TreeCache(client, "/Services");
//创建服务注册和变化的监听
TreeCacheListener cacheListener = new TreeCacheListener() {
@Override
public void childEvent(CuratorFramework client, TreeCacheEvent event)
throws Exception {
System.out.println("事件类型:" + event.getType());
if (null != event.getData()) {
System.out.println("节点数据:" + event.getData().getPath() + ":" + new String(event.getData().getData(), "UTF-8"));
}
}
};
//注册缓存监听
cache.getListenable().addListener(cacheListener);
//启动cache
cache.start();
}
//获取服务列表
public Map getServices(){
return cache.getCurrentChildren("/Services");
}
//服务注册中心关闭
public void destory() throws IOException{
if(latch != null && (!LeaderLatch.State.CLOSED.equals(latch.getState()))){
latch.close();
}
if(cache != null){
cache.close();
}
if(client != null && (!CuratorFrameworkState.STOPPED.equals(client.getState()))){
client.close();
}
}
public static void main(String[] args) throws IOException {
ServiceCenter center = new ServiceCenter("172.16.3.201:2181");
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
center.destory();
}
}
要注册的服务中在服务启动时调用注册方法,注册方法如下:
import java.io.IOException;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.imps.CuratorFrameworkState;
import org.apache.curator.framework.recipes.leader.LeaderLatch;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
//在服务启动时创建这个对象并调用register方法
public class ServiceRegister {
private CuratorFramework client;
private String connectString = "172.16.3.201:2181";
private LeaderLatch latch;
public ServiceRegister(String connectString) {
super();
if(connectString != null && (!"".equals(connectString))){
this.connectString = connectString;
}
try {
init();
} catch (Exception e) {
e.printStackTrace();
}
}
//注册器初始化
private void init() throws Exception {
client = CuratorFrameworkFactory.builder()
.connectString(connectString)
.sessionTimeoutMs(5000)
.connectionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();
client.start();
latch = new LeaderLatch(client, "/Services");
latch.start();
}
//注册器销毁
public void destory() throws IOException {
if(latch != null && (!LeaderLatch.State.CLOSED.equals(latch.getState()))){
latch.close();
}
if(client != null && (!CuratorFrameworkState.STOPPED.equals(client.getState()))){
client.close();
}
}
//注册服务
public boolean register(ServiceInstance instance) throws Exception{
String servicePath = "/Services/"+instance.getService().getName();
//如果服务不存在则创建
if(client.checkExists().forPath(servicePath) == null){
//这里不能创建临时节点,因为临时节点下不能创建节点
client.create().withMode(CreateMode.PERSISTENT).forPath(servicePath, instance.getService().getData().getBytes());
}
//如果服务存在但children为空则更新服务信息
else if(client.getChildren().forPath(servicePath) == null || client.getChildren().forPath(servicePath).size() == 0){
client.setData().forPath(servicePath, instance.getService().getData().getBytes());
}
String instancePath = servicePath + "/" + instance.getHost()+ ":" +instance.getPort();
//如果实例不存在则创建
if(client.checkExists().forPath(instancePath) == null){
//这里创建的都是临时节点,当client断开连接了之后节点删除,服务注册中心会监听到节点删除事件
client.create().withMode(CreateMode.EPHEMERAL).forPath(instancePath, instance.getData().getBytes());
}
//否则更新实例信息
else{
client.setData().forPath(instancePath, instance.getData().getBytes());
}
return true;
}
public static void main(String[] args) throws InterruptedException, IOException {
ServiceRegister register = new ServiceRegister("172.16.3.201:2181");
Service service = new Service();
service.setName("demo1");
service.setData("ssssssssssss");
ServiceInstance instance = new ServiceInstance();
instance.setData("cccccccccccccc");
instance.setHost("127.0.0.1");
instance.setPort("8080");
instance.setService(service);
try {
register.register(instance);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Thread.sleep(2000);
register.destory();
}
}
(待完成)
当zookeeper客户端连接到一个zookeeper的集群时,这个客户端连接到集群中的一个节点(一个服务器),这个节点可能是leader也可能是follower。连接后该节点会向客户端发送确认并分配会话id,如果客户端没有收到确认,会向集群的其他节点发送连接请求,连接成功后客户端会定时向节点发送心跳以确认连接仍在。
当客户端请求获取一个Znode信息时,与leader无关,直接请求具有该Znode的节点得到。
当客户端要增加一个Znode时,该节点将请求转发给leader节点,然后由leader节点向所有节点发出请求,大部分节点响应则请求成功。
zookeeper集群的leader选举过程(摘自https://www.w3cschool.cn/zookeeper/zookeeper_leader_election.html):
https://www.cnblogs.com/LUA123/p/7222216.html