zookeeper使用

一、安装zooLeeper和zkServer、zkCli使用

以下是linux环境的安装和使用

1.安装

先要安装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

2.启动

bin/zkServer.sh start

3.zkCli操作

启动客户端

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

 

二、java操作

1.使用Apache.ZooKeeper操作

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();
	}
}

2.使用Curator操作

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");
	}
}

3.Curator高级特性

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();
	}
}

三、基于zookeeper的服务注册和发现

1.代码示例

服务注册中心示例代码:

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();
	}
}

2.集成springcloud

(待完成)

四、zookeeper的特性和工作方式

当zookeeper客户端连接到一个zookeeper的集群时,这个客户端连接到集群中的一个节点(一个服务器),这个节点可能是leader也可能是follower。连接后该节点会向客户端发送确认并分配会话id,如果客户端没有收到确认,会向集群的其他节点发送连接请求,连接成功后客户端会定时向节点发送心跳以确认连接仍在。

当客户端请求获取一个Znode信息时,与leader无关,直接请求具有该Znode的节点得到。

当客户端要增加一个Znode时,该节点将请求转发给leader节点,然后由leader节点向所有节点发出请求,大部分节点响应则请求成功。

zookeeper集群的leader选举过程(摘自https://www.w3cschool.cn/zookeeper/zookeeper_leader_election.html):

  • 所有节点创建具有相同路径 /app/leader_election/guid_ 的顺序、临时节点。
  • ZooKeeper集合将附加10位序列号到路径,创建的znode将是 /app/leader_election/guid_0000000001,/app/leader_election/guid_0000000002等。
  • 对于给定的实例,在znode中创建最小数字的节点成为leader,而所有其他节点是follower。
  • 每个follower节点监视下一个具有最小数字的znode。例如,创建znode/app/leader_election/guid_0000000008的节点将监视znode/app/leader_election/guid_0000000007,创建znode/app/leader_election/guid_0000000007的节点将监视znode/app/leader_election/guid_0000000006。
  • 如果leader关闭,则其相应的znode/app/leader_electionN会被删除。
  • 下一个在线follower节点将通过监视器获得关于leader移除的通知。
  • 下一个在线follower节点将检查是否存在其他具有最小数字的znode。如果没有,那么它将承担leader的角色。否则,它找到的创建具有最小数字的znode的节点将作为leader。
  • 类似地,所有其他follower节点选举创建具有最小数字的znode的节点作为leader。

五、zookeeper集群实现

https://www.cnblogs.com/LUA123/p/7222216.html

你可能感兴趣的:(zookeeper)