一.部署与运行
1.系统环境
zk支持绝大多少操作系统.不过需要注意的是,FreeBsd系统的JVM对JAVA的NIO支持的不是很好,不建议在该系统上部署生产环境.
2.java环境
建议使用jdk1.6或者以上的版本.
3.集群或者单机
3.1集群模式需要至少三台机器, jdk和zk都下载配置好之后.修改$ZK_HOME/conf下面的zoo_sample.cfg
- tickTime=2000
- initLimit=5
- syncLimit=2
- dataDir=/var/lib/zk/
- dataLogDir=/var/lib/zk/logs
- clientPort=2181
- #0代表server id,在datadir下创建一个myid文件,里面的内容就是这个id
- server.0=192.168.1.10:8880:7770
- server.1=192.168.1.11:8881:7771
- server.2=192.168.1.12:8882:7772
接着在datadir目录下创建myid文件,内容就是server.id
接着使用$ZK_HOME/bin里面的zkServer.sh启动,sh zkServer.sh start
用命令echo stat | nc localhost 2181测试是否启动成功(失败有可能是防火墙的原因)
3.1平常测试的时候只要使用单机模式即可
- tickTime=2000
- initLimit=5
- syncLimit=2
- dataDir=/var/lib/zk/
- dataLogDir=/var/lib/zk/logs
- clientPort=2181
- #0代表server id,在datadir下创建一个myid文件,里面的内容就是这个id
- server.1=127.0.0.1:8880:7770
二.zk的客户端
客户端的启动命令: sh zkCli -server ip:port
创建节点:create [-s] [-e] path data acl s代表持久节点,e代表临时节点,acl代表权限.默认创建持久节点.例如: create /zknode 123 digest: username:password
列出路径下的子节点: ls [path] .path代表路径 。 ls /zknode
获取节点的属性: get [path]
更新节点属性: set [path] data [version]
删除节点: delete path [version]
设置节点acl: setAcl path acl .例如: setAcl path acl.
三.zk java客户端
1.连接客户端
下面的代码是一个基本的zk java客户端的代码
public class ZkTest implements Watcher{
private static CountDownLatch cdl = new CountDownLatch(1);
public static void main(String[] args) throws IOException {
ZooKeeper zk = new ZooKeeper("192.168.43.191:2181", 5000, new ZkTest());
System.out.println(zk.getState());
try {
cdl.await();
} catch (InterruptedException e) {
}
System.out.println("connect");
}
@Override
public void process(WatchedEvent arg0) {
System.out.println("事件:"+arg0+";"+arg0.getState());
if(KeeperState.SyncConnected == arg0.getState()){
System.out.println(arg0.getState());
cdl.countDown();
}
}
}
下面的代码是zk session的复用
public class Zk_ID_PWD implements Watcher{
private static CountDownLatch cdl = new CountDownLatch(1);
public static void main(String[] args) throws IOException, InterruptedException {
ZooKeeper zk = new ZooKeeper("192.168.43.191:2181",
5000, new Zk_ID_PWD());
cdl.await();
long id = zk.getSessionId();
byte[] pwd = zk.getSessionPasswd();
//连接失败
zk = new ZooKeeper("192.168.43.191:2181",
5000, new Zk_ID_PWD(),1l,"test".getBytes());
//连接成功
zk = new ZooKeeper("192.168.43.191:2181",
5000, new Zk_ID_PWD(),id,pwd);
Thread.sleep(Integer.MAX_VALUE);
}
@Override
public void process(WatchedEvent arg0) {
System.out.println("事件:"+arg0+";"+arg0.getState());
if(KeeperState.SyncConnected == arg0.getState()){
System.out.println(arg0.getState());
cdl.countDown();
}
}
}
2.创建节点
public class Zk_Create implements Watcher {
private static CountDownLatch cdl = new CountDownLatch(1);
private static ZooKeeper zk = null;
static {
try {
zk = new ZooKeeper("192.168.43.191:2181", 5000, new Zk_Create());
System.out.println(zk.getState());
} catch (IOException e1) {
// TODO 请把关键信息(包括堆栈)记入日志,并删除下面一行。
e1.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
Zk_Create zc = new Zk_Create();
try {
cdl.await();
} catch (InterruptedException e) {
}
System.out.println("connect");
// zc.createTempNode();
zc.createTempNodeAsync();
}
// 创建临时节点
public void createTempNode() throws KeeperException, InterruptedException {
String path = zk.create("/zcf", "zcf".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
System.out.println("成功创建节点:" + path);
String path1 = zk.create("/zcf", "zcf".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println("成功创建节点:" + path1);
}
public void createTempNodeAsync() throws Exception{
zk.create("/zcf", "zcf".getBytes(),
Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL
,new IStringBack(),"async");
Thread.sleep(Integer.MAX_VALUE);
}
public class IStringBack implements AsyncCallback.StringCallback{
@Override
public void processResult(int arg0, String arg1, Object arg2, String arg3) {
System.out.println("create path result:"+ arg0+","+arg1+","+arg2+","+arg3);
}
}
@Override
public void process(WatchedEvent arg0) {
System.out.println("事件:" + arg0 + ";" + arg0.getState());
if (KeeperState.SyncConnected == arg0.getState()) {
System.out.println(arg0.getState());
cdl.countDown();
}
}
}
3.权限设置以及授权
public class Zk_Auth implements Watcher{
final static String path = "/zcf_auth";
final static String path1 = path + "/child";
private static CountDownLatch cdl = new CountDownLatch(3);
public static void main(String[] args) throws Exception {
Zk_Auth za = new Zk_Auth();
//za.testAuth();
za.testDelete();
}
//删除权限的设置,不影响删除没有子节点的节点
public void testAuth() throws Exception{
ZooKeeper zk = ZKUtil.getInstance(null, new Zk_Auth());
ZooKeeper zk1 = ZKUtil.getInstance(null, new Zk_Auth());
ZooKeeper zk2 = ZKUtil.getInstance(null, new Zk_Auth());
cdl.await();
System.out.println("connect");
//第二个参数类似username:pwd
zk.addAuthInfo("digest", "zcf:true".getBytes());
zk.create(path, "zcf".getBytes(),
Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT);
zk.create(path, "zcf".getBytes(),
Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT);
zk1.addAuthInfo("digest", "zcf:true".getBytes());
System.out.println(zk1.getData(path, false, null));
zk2.addAuthInfo("digest", "zcf:false".getBytes());
//会爆没权限错误
System.out.println(zk2.getData(path, false, null));
}
public void testDelete() throws IOException, Exception{
ZooKeeper zk = ZKUtil.getInstance(null, new Zk_Auth());
ZooKeeper zk1 = ZKUtil.getInstance(null, new Zk_Auth());
ZooKeeper zk2 = ZKUtil.getInstance(null, new Zk_Auth());
cdl.await();
System.out.println("connect");
//第二个参数类似username:pwd
zk.addAuthInfo("digest", "zcf:true".getBytes());
zk.create(path, "zcf".getBytes(),
Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT);
zk.create(path1, "zcf".getBytes(),
Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT);
try {
zk1.delete(path1, -1);
} catch (Exception e) {
e.printStackTrace();
}
zk2.addAuthInfo("digest", "zcf:true".getBytes());
//会爆没权限错误
zk2.delete(path1, -1);
System.out.println("成功删除节点:"+path1);
zk1.delete(path, -1);
System.out.println("成功删除节点:"+path);
}
@Override
public void process(WatchedEvent arg0) {
System.out.println("事件:" + arg0 + ";" + arg0.getState());
if (KeeperState.SyncConnected == arg0.getState()) {
System.out.println(arg0.getState());
cdl.countDown();
}
}
}
二.curator
1.master选举
应用场景:定时任务只需要在一台机器上执行,通过master选择功能,选择其中一台执行.
原理:每个zk客户端会在指定path下生产一个类似这样的path.节点序号递增,每次节点最小的成为leader.然后每次执行完任务之后,抢到资源的节点会自己删除,然后重新生产一个节点,继续和其他节点竞争.
[_c_54dd51db-0e4a-4fdf-9251-f39e88102a87-lock-0000000121,
_c_af3197ec-da19-4812-b20d-bb3a010be549-lock-0000000118,
_c_9a0edc16-5489-4d95-b24b-e1610c133bbe-lock-0000000120]
反思:
我们都知道,通过Leader选举可以选举出一台机器来执行定时任务.每次到执行Job的时候重新进行一次竞选,成为Leader者进行执行。
这里就需要考虑一下问题:
1 . 第二种方案如果服务器的时间不一致如何处理?如果每台机器Job执行的时间不一致如何处理?2. 定时任务的幂等性保证.(token)
public class Zk_Curator_Master {
public static void main(String[] args) throws InterruptedException {
for(int i = 0;i< 6;i++){
CuratorFramework cf = CuratorFrameworkFactory.newClient("192.168.43.191:2181",
new ExponentialBackoffRetry(1000, 3));
cf.start();
String name ="client #"+i;
LeaderSelector s = new LeaderSelector(cf,
"/zcf/master_test", new LeaderSelectorListener() {
@Override
public void stateChanged(CuratorFramework client, ConnectionState newState) {
if(newState == ConnectionState.SUSPENDED||ConnectionState.LOST == newState){
throw new CancellationException();
}
}
@Override
public void takeLeadership(CuratorFramework arg0) throws Exception {
System.out.println(name+"成为master角色");
Thread.sleep(3000);
System.out.println(name+"完成master任务");
}
});
s.autoRequeue();
s.start();
Thread.sleep(Integer.MAX_VALUE);
}
}
2.分布式锁以及分布式计数器
public class ZK_Distributed_Lock {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
normalLockTest();
DistributedLock();
}
public static void DistributedLock(){
CuratorFramework cf = CuratorFrameworkFactory.newClient("192.168.43.191:2181",
new ExponentialBackoffRetry(1000, 3));
cf.start();
final InterProcessMutex lock = new InterProcessMutex(
cf, "/zcf/distributeLock");
CountDownLatch cdl = new CountDownLatch(1);
for(int i = 0 ;i< 30 ;i++){
new Thread(new Runnable() {
@Override
public void run() {
try {
cdl.await();
lock.acquire();
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss/SSS");
System.out.println(sdf.format(new Date()));
} catch (InterruptedException e) {
// TODO 请把关键信息(包括堆栈)记入日志,并删除下面一行。
e.printStackTrace();
} catch (Exception e) {
// TODO 请把关键信息(包括堆栈)记入日志,并删除下面一行。
e.printStackTrace();
}finally{
try {
lock.release();
} catch (Exception e) {
// TODO 请把关键信息(包括堆栈)记入日志,并删除下面一行。
e.printStackTrace();
}
}
}
}).start();
}
cdl.countDown();
}
public static void normalLockTest(){
CountDownLatch cdl = new CountDownLatch(1);
for(int i = 0 ;i< 10;i++){
new Thread(new Runnable() {
@Override
public void run() {
try {
cdl.await();
lock.lock();
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss/SSS");
System.out.println(sdf.format(new Date()));
} catch (InterruptedException e) {
// TODO 请把关键信息(包括堆栈)记入日志,并删除下面一行。
e.printStackTrace();
}finally {
lock.unlock();
}
}
}).start();
}
cdl.countDown();
}
}
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.ReentrantLock;
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.DistributedAtomicInteger;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.retry.RetryNTimes;
public class ZK_Distributed_Count {
public static void main(String[] args) {
DistributedCount();
}
public static void DistributedCount(){
CuratorFramework cf = CuratorFrameworkFactory.newClient("192.168.43.191:2181",
new ExponentialBackoffRetry(1000, 3));
cf.start();
CountDownLatch cdl = new CountDownLatch(1);
for(int i = 0 ;i< 30 ;i++){
final DistributedAtomicInteger d = new DistributedAtomicInteger(
cf, "/zcf/distributeCount",new RetryNTimes(3, 1000));
new Thread(new Runnable() {
@Override
public void run() {
try {
cdl.await();
AtomicValue<Integer> value = d.add(1);
while(!value.succeeded()){
value = d.add(1);
}
System.out.println(value.postValue());
} catch (InterruptedException e) {
// TODO 请把关键信息(包括堆栈)记入日志,并删除下面一行。
e.printStackTrace();
} catch (Exception e) {
// TODO 请把关键信息(包括堆栈)记入日志,并删除下面一行。
e.printStackTrace();
}
}
}).start();
}
cdl.countDown();
}
}