zookeeper實現廣播
一、簡介
很多時候會遇到這樣的場景:一個消息或者通知需要通知給所有集羣中所有的節點,這些節點收到通知後執行一定邏輯。
使用方不得持續高頻(一分鐘大於10次)投遞通知
使用方不得投遞體積過大的數據,不得大於512KB
使用方不得濫用資源
二、安裝zookeeper
啓動命令
bin/zookeeper-server-start.sh config/zookeeper.properties
三、Maven依賴
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-client</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.1.35</version>
</dependency>
四、監聽客戶端代碼
package zookeeper;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.NodeCache;
import org.apache.curator.framework.recipes.cache.NodeCacheListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.utils.ZKPaths;
import org.apache.zookeeper.ZooKeeper;
import java.nio.charset.Charset;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Created by zhengyong on 16/11/24.
*/
public class Listener {
private static final int sessionTimeout = 15000;
private static final String ZK_HOST = "127.0.0.1:2181";
private static final String ZK_PATH = "/zkPath";
private static ExecutorService executorService = Executors.newFixedThreadPool(5);
public static void main(String[] args) throws Exception {
// 創建node
ZooKeeper zookeeper = new ZooKeeper(ZK_HOST, sessionTimeout, null);
ZKPaths.mkdirs(zookeeper, ZK_PATH);
// 創建5個listener
for (int i = 0; i < 5; i++) {
final int index = i + 1;
executorService.execute(new Runnable() {
@Override
public void run() {
try {
createListeners("Thread-" + index);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
// 模擬程序運行不中斷
Thread.sleep(100000);
executorService.shutdown();
}
/**
* 創建listener
*
* @param threadName 線程名稱
* @throws Exception
*/
private static void createListeners(final String threadName) throws Exception {
CuratorFramework localCuratorClient = CuratorFrameworkFactory.builder().connectString(ZK_HOST).sessionTimeoutMs(sessionTimeout).retryPolicy(new ExponentialBackoffRetry(1000,
10, 5000)).build();
localCuratorClient.start();
final NodeCache nodeCache = new NodeCache(localCuratorClient, ZK_PATH);
nodeCache.getListenable().addListener(new NodeCacheListener() {
@Override
public void nodeChanged() throws Exception {
final byte[] curr = nodeCache.getCurrentData().getData();// 取到最新的數據
System.out.println(String.format("%s收到客戶端通知: %s", threadName, new String(curr, Charset.forName("UTF-8"))));
// 業務邏輯
}
});
// 一定要start()
nodeCache.start();
System.out.println(String.format("listener %s start", threadName));
// 系統關閉的時候請調用nodeCache.close();
}
}
五、通知客戶端代碼
package zookeeper;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.utils.ZKPaths;
import org.apache.zookeeper.ZooKeeper;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.data.Stat;
import java.nio.charset.Charset;
import java.util.Map;
/**
* Created by zhengyong on 16/11/24.
*/
public class Notify {
private static final int sessionTimeout = 15000;
private static final String ZK_HOST = "1127.0.0.1:2181";
private static final String ZK_PATH = "/zkPath";
public static void main(String[] args) throws Exception {
// 創建node
ZooKeeper zookeeper = new ZooKeeper(ZK_HOST, sessionTimeout, null);
ZKPaths.mkdirs(zookeeper, ZK_PATH);
CuratorFramework localCuratorClient = CuratorFrameworkFactory.builder().connectString(ZK_HOST)
.sessionTimeoutMs(sessionTimeout).retryPolicy(new ExponentialBackoffRetry(1000,
10,
5000)).build();
localCuratorClient.start();
final Map<String, Object> data = Maps.newHashMap();
data.put("version", System.currentTimeMillis());
data.put("data", "我想刪除第一條數據");// 放入業務數據
final Stat stat = new Stat();
final byte[] oldData = localCuratorClient.getData().storingStatIn(stat).forPath(ZK_PATH); // 先讀區舊數據
System.out.println(String.format("oldData: %s ", new String(oldData, Charset.forName("UTF-8"))));
System.out.println(String.format("newData: %s", JSON.toJSONString(data)));
// 若不需要處理舊數據就直接往下
byte[] newData = JSON.toJSONString(data).getBytes(Charset.forName("UTF-8"));
// 防止併發問題
final Stat newStat = localCuratorClient.setData().withVersion(stat.getVersion()).forPath(ZK_PATH, newData);
System.out.println(String.format("write data result :%s", JSON.toJSONString(newStat)));
// 模擬程序運行不中斷
Thread.sleep(100000);
}
}