参考资料
http://curator.apache.org/curator-recipes/path-cache.html‘
http://ifeve.com/zookeeper-path-cache/
可利用Zookeeper在集群的各个节点之间数据。每个节点可以得到最新的缓存的数据。分为三种:Path Cache,Node Cache,Tree Cache
Path Cache
Path Cache主要用来监听Zonde的变化 。 增加、更新或者删除一个zNode,path Cache会随之进行更新:包含新的children集合,children上的数据,children状态。
相关的类
- PathChildrenCache : 主要类
- PathChildrenCacheEvent : 事件
- PathChildrenCacheListener : 监听 器
- ChildData
使用
创建实例
path即监听的path, cacheData如果设为true,则除了缓存节点状态外,节点上的数据也会被缓存 。
public PathChildrenCache(CuratorFramework client,
String path,
boolean cacheData)
Parameters:
client - the client
path - path to watch
cacheData - if true, node contents are cached in addition to the stat
启动
必须首先通过start()方法启动cache,用完后必须调用close()关闭cache。
start()方法有两个,不带参数的start()会进行默认操作。
start(StartMode)可带一个枚举类型的startMode,设置start的方式
public enum StartMode
{
/**
* cache will _not_ be primed. i.e. it will start empty and you will receive
* events for all nodes added, etc.
*/
NORMAL,
/**
* rebuild() will be called before this method returns in
* order to get an initial view of the node.
*/
BUILD_INITIAL_CACHE,
/**
* After cache is primed with initial values (in the background) a
* PathChildrenCacheEvent.Type.INITIALIZED event will be posted
*/
POST_INITIALIZED_EVENT
}
- NORMAL: 初始时为空。
- BUILD_INITIAL_CACHE: 在这个方法返回之前调用rebuild()。
- POST_INITIALIZED_EVENT: 当Cache初始化数据后发送一个PathChildrenCacheEvent.Type#INITIALIZED事件
获取数据
可通过getCurrentData()返回一个List对象,可以遍历所有的子节点。同时也可设置一个监听:在cache.getListenerable().addListener();
public void addListener(PathChildrenCacheListener listener)
Add a change listener
Parameters:
listener - the listener
错误处理
PathChildrenCache实例监听了ConnectionStateListener。如果有连接状态改变时,cache会被重置RESET,即PathChildrenCacheListener会收到重置操作。
代码示例
启动pathCache,同时在cache上设置监听( cache.getListenable().addListener(listener);), 根据输入进行相关的操作。
cache.getCurrentData(): 获取当前节点上存储的数据 。
设置/更新、移除其实是使用client (CuratorFramework)来操作, 不通过PathChildrenCache操作:
client.setData().forPath(path, bytes);
client.create().creatingParentsIfNeeded().forPath(path, bytes);
client.delete().forPath(path);
查询缓存:
for (ChildData data : cache.getCurrentData()) {
System.out.println(data.getPath() + " = " + new String(data.getData()));
}
代码来自于官方
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package cache;
import com.google.common.collect.Lists;
import org.apache.curator.utils.CloseableUtils;
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.retry.ExponentialBackoffRetry;
import org.apache.curator.test.TestingServer;
import org.apache.curator.utils.ZKPaths;
import discovery.ExampleServer;
import org.apache.zookeeper.KeeperException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.List;
/**
* An example of the PathChildrenCache. The example "harness" is a command processor
* that allows adding/updating/removed nodes in a path. A PathChildrenCache keeps a
* cache of these changes and outputs when updates occurs.
*/
public class PathCacheExample
{
private static final String PATH = "/example/cache";
public static void main(String[] args) throws Exception
{
TestingServer server = new TestingServer();
CuratorFramework client = null;
PathChildrenCache cache = null;
try
{
client = CuratorFrameworkFactory.newClient(server.getConnectString(), new ExponentialBackoffRetry(1000, 3));
client.start();
// in this example we will cache data. Notice that this is optional.
cache = new PathChildrenCache(client, PATH, true);
cache.start();
processCommands(client, cache);
}
finally
{
CloseableUtils.closeQuietly(cache);
CloseableUtils.closeQuietly(client);
CloseableUtils.closeQuietly(server);
}
}
private static void addListener(PathChildrenCache cache)
{
// a PathChildrenCacheListener is optional. Here, it's used just to log changes
PathChildrenCacheListener listener = new PathChildrenCacheListener()
{
@Override
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception
{
switch ( event.getType() )
{
case CHILD_ADDED:
{
System.out.println("Node added: " + ZKPaths.getNodeFromPath(event.getData().getPath()));
break;
}
case CHILD_UPDATED:
{
System.out.println("Node changed: " + ZKPaths.getNodeFromPath(event.getData().getPath()));
break;
}
case CHILD_REMOVED:
{
System.out.println("Node removed: " + ZKPaths.getNodeFromPath(event.getData().getPath()));
break;
}
}
}
};
cache.getListenable().addListener(listener);
}
private static void processCommands(CuratorFramework client, PathChildrenCache cache) throws Exception
{
// More scaffolding that does a simple command line processor
printHelp();
List<ExampleServer> servers = Lists.newArrayList();
try
{
addListener(cache);
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
boolean done = false;
while ( !done )
{
System.out.print("> ");
String line = in.readLine();
if ( line == null )
{
break;
}
String command = line.trim();
String[] parts = command.split("\\s");
if ( parts.length == 0 )
{
continue;
}
String operation = parts[0];
String args[] = Arrays.copyOfRange(parts, 1, parts.length);
if ( operation.equalsIgnoreCase("help") || operation.equalsIgnoreCase("?") )
{
printHelp();
}
else if ( operation.equalsIgnoreCase("q") || operation.equalsIgnoreCase("quit") )
{
done = true;
}
else if ( operation.equals("set") )
{
setValue(client, command, args);
}
else if ( operation.equals("remove") )
{
remove(client, command, args);
}
else if ( operation.equals("list") )
{
list(cache);
}
Thread.sleep(1000); // just to allow the console output to catch up
}
}
finally
{
for ( ExampleServer server : servers )
{
CloseableUtils.closeQuietly(server);
}
}
}
private static void list(PathChildrenCache cache)
{
if ( cache.getCurrentData().size() == 0 )
{
System.out.println("* empty *");
}
else
{
for ( ChildData data : cache.getCurrentData() )
{
System.out.println(data.getPath() + " = " + new String(data.getData()));
}
}
}
private static void remove(CuratorFramework client, String command, String[] args) throws Exception
{
if ( args.length != 1 )
{
System.err.println("syntax error (expected remove <path>): " + command);
return;
}
String name = args[0];
if ( name.contains("/") )
{
System.err.println("Invalid node name" + name);
return;
}
String path = ZKPaths.makePath(PATH, name);
try
{
client.delete().forPath(path);
}
catch ( KeeperException.NoNodeException e )
{
// ignore
}
}
private static void setValue(CuratorFramework client, String command, String[] args) throws Exception
{
if ( args.length != 2 )
{
System.err.println("syntax error (expected set <path> <value>): " + command);
return;
}
String name = args[0];
if ( name.contains("/") )
{
System.err.println("Invalid node name" + name);
return;
}
String path = ZKPaths.makePath(PATH, name);
byte[] bytes = args[1].getBytes();
try
{
client.setData().forPath(path, bytes);
}
catch ( KeeperException.NoNodeException e )
{
client.create().creatingParentContainersIfNeeded().forPath(path, bytes);
}
}
private static void printHelp()
{
System.out.println("An example of using PathChildrenCache. This example is driven by entering commands at the prompt:\n");
System.out.println("set <name> <value>: Adds or updates a node with the given name");
System.out.println("remove <name>: Deletes the node with the given name");
System.out.println("list: List the nodes/values in the cache");
System.out.println("quit: Quit the example");
System.out.println();
}
}
输出:
An example of using PathChildrenCache. This example is driven by entering commands at the prompt:
set <name> <value>: Adds or updates a node with the given name
remove <name>: Deletes the node with the given name
list: List the nodes/values in the cache
quit: Quit the example
> set test testdata
Node added: test
>
Node Cache
Node Cache用来监听 Znode。 数据改变或者Znode被删除,会触发node cache, 数据改变时node cache会改变其状态存储新值,znode删除时 node cache会置为null .
相关的类
- NodeCache
- NodeCacheListener
- ChildData
使用方法
创建实例
public NodeCache(CuratorFramework client,
String path)
Parameters:
client - the client
path - path to cache
启动与关闭
start() 启动,使用完成 后,需调用 close()关闭
getCurrentData()获取cache最新状态。也可以通过cahce.getListenerable().addListerner()增加监听 。
public void addListener(NodeCacheListener listener)
Add a change listener
Parameters:
listener - the listener
错误处理
NodeCache实例监听ConnectionStateListener。可通过其源码看出来,其start()方法中client.getConnectionStateListenable().addListener(connectionStateListener);
/**
* Start the cache. The cache is not started automatically. You must call this method.
*
* @param mode Method for priming the cache
* @throws Exception errors
*/
public void start(StartMode mode) throws Exception
{
Preconditions.checkState(state.compareAndSet(State.LATENT, State.STARTED), "already started");
mode = Preconditions.checkNotNull(mode, "mode cannot be null");
client.getConnectionStateListenable().addListener(connectionStateListener);
switch ( mode )
{
case NORMAL:
{
offerOperation(new RefreshOperation(this, RefreshMode.STANDARD));
break;
}
case BUILD_INITIAL_CACHE:
{
rebuild();
break;
}
case POST_INITIALIZED_EVENT:
{
initialSet.set(Maps.<String, ChildData>newConcurrentMap());
offerOperation(new RefreshOperation(this, RefreshMode.POST_INITIALIZED));
break;
}
}
}
代码示例
package cache;
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.test.TestingServer;
import org.apache.curator.utils.CloseableUtils;
import org.apache.zookeeper.KeeperException;
public class NodeCacheExample {
private static final String PATH = "/example/cache";
public static void main(String[] args) throws Exception {
NodeCache cache = null;
CuratorFramework client = null;
try (TestingServer server = new TestingServer()) {
client = CuratorFrameworkFactory.newClient(server
.getConnectString(), new ExponentialBackoffRetry(1000, 3));
cache = new NodeCache(client, PATH);
client.start();
cache.start();
addListener(cache);
// 新建节点
String oldData = "oldtestData";
try {
client.setData().forPath(PATH, oldData.getBytes());
} catch (KeeperException.NoNodeException e) {
client.create().creatingParentContainersIfNeeded()
.forPath(PATH, oldData.getBytes());
}
// 修改节点上数据
String newData = "newtestData";
try {
client.setData().forPath(PATH, newData.getBytes());
} catch (KeeperException.NoNodeException e) {
client.create().creatingParentsIfNeeded()
.forPath(PATH, newData.getBytes());
}
// 删除节点
try {
client.delete().forPath(PATH);
} catch (Exception e) {
e.printStackTrace();
}
} finally {
CloseableUtils.closeQuietly(cache);
CloseableUtils.closeQuietly(client);
}
}
private static void addListener(final NodeCache cache) {
NodeCacheListener listener = new NodeCacheListener() {
@Override
public void nodeChanged() throws Exception {
if (cache.getCurrentData() != null) {
System.out.println("Node changed: "
+ cache.getCurrentData().getPath() + ", value: "
+ new String(cache.getCurrentData().getData()));
}
}
};
cache.getListenable().addListener(listener);
}
}
Tree Cache
Tree Cache监控节点的状态,包括子节点的状态。相当于Path Cache 与Node Cache的组合。
相关 的类
- TreeCache
- TreeCacheListener
- TreeCacheEvent
- ChildData
用法
创建实例
public TreeCache(CuratorFramework client,
String path)
Parameters:
client - the client
path - path to watch
启动与停止
通过start() 启动,完成后通过close()关闭。
可调用 getCurrentChildren()获取cache目前的状态,类型为Map
public void addListener(TreeCacheListener listener)
Add a change listener
Parameters:
listener - the listener
错误处理
TreeCache内部监控ConnectionStateListener。如果发生连接状态改变时,cache会收到相应的改变信息。
代码示例
新建节点,修改节点数据,新建子节点及删除节点时都将触发TreeCacheListener.
package cache;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
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.retry.ExponentialBackoffRetry;
import org.apache.curator.test.TestingServer;
import org.apache.curator.utils.CloseableUtils;
import org.apache.curator.utils.ZKPaths;
public class TreeCacheExample {
private static final String PATH = "/examples/cache";
private static final String CHILD="/examples/cache/child";
public static void main(String[] args) throws Exception {
CuratorFramework client = null;
TreeCache cache = null;
try (TestingServer server = new TestingServer()) {
client = CuratorFrameworkFactory.newClient(server
.getConnectString(), new ExponentialBackoffRetry(1000, 3));
cache = new TreeCache(client, PATH);
client.start();
cache.start();
addListener(cache);
String oldData = "oldtestData";
String newData = "newtestData";
try {
// 新建节点
client.create().creatingParentContainersIfNeeded().forPath(PATH, oldData.getBytes());
// 修改节点上数据
client.setData().forPath(PATH, newData.getBytes());
//新建子节点
client.create().creatingParentContainersIfNeeded().forPath(CHILD, "childData".getBytes());
// 删除节点
client.delete().forPath(CHILD);
client.delete().forPath(PATH);
} catch (Exception e) {
e.printStackTrace();
}
}finally{
CloseableUtils.closeQuietly(cache);
CloseableUtils.closeQuietly(client);
}
}
public static void addListener(TreeCache cache) {
TreeCacheListener listener = new TreeCacheListener() {
@Override
public void childEvent(CuratorFramework client, TreeCacheEvent event)
throws Exception {
switch (event.getType()) {
case NODE_ADDED: {
System.out
.println("Node added: "
+ ZKPaths.getNodeFromPath(event.getData()
.getPath()));
break;
}
case NODE_UPDATED: {
System.out
.println("Node updated: "
+ ZKPaths.getNodeFromPath(event.getData()
.getPath()));
break;
}
case NODE_REMOVED: {
System.out
.println("Node removed: "
+ ZKPaths.getNodeFromPath(event.getData()
.getPath()));
break;
}
default:{
System.out.println("other event:"+event.getType());
break;
}
}
}
};
cache.getListenable().addListener(listener);
}
}
输出结果:
other event:INITIALIZED
Node added: cache
Node updated: cache
Node added: child
Node removed: child
Node removed: cache