Zookeeper建立连接:
需要注意的是,zk在main方法中运行的时候,因为zk的连接的异步进行的,是一个守护线程,如果我们主线程停止,守护线程也会跟着停止,会导致我们根本感应不到zk连接建立main方法就结束了。这里 使用countDownLatch来等待连接建立完成后再继续往下走;
查看到我们zk的状态为
SyncConnected:代表连接建立好了
type:None;‘
通过这些事件感知到连接已经建立好了;
代码实现如下:
private static ZooKeeper zooKeeper=null;
private static CountDownLatch countDownLatch=new CountDownLatch(1);
private final static String CONNECT_STR="192.168.88.3:2181";
private final static Integer SESSION_TIMEOUT=30*1000;
public static void main(String[] args) throws Exception{
zooKeeper=new ZooKeeper(CONNECT_STR, SESSION_TIMEOUT, new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getType()== Event.EventType.None
&& event.getState() == Event.KeeperState.SyncConnected){
log.info("连接已建立");
countDownLatch.countDown();
}
}
});
countDownLatch.await();
}
zk创建节点及监听
在上面代码的基础上我们写一个关于创建节点,并且监听节点的改变;
MyConfig myConfig = new MyConfig();
myConfig.setKey("anykey");
myConfig.setName("anyName");
ObjectMapper objectMapper=new ObjectMapper();
byte[] bytes = objectMapper.writeValueAsBytes(myConfig);
String s = zooKeeper.create("/myconfig", bytes, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
Watcher watcher = new Watcher() {
@SneakyThrows
@Override
public void process(WatchedEvent event) {
if (event.getType()== Event.EventType.NodeDataChanged
&& event.getPath()!=null && event.getPath().equals("/myconfig")){
log.info(" PATH:{} 发生了数据变化" ,event.getPath());
byte[] data = zooKeeper.getData("/myconfig", this, null);
MyConfig newConfig = objectMapper.readValue(new String(data), MyConfig.class);
log.info("数据发生变化: {}",newConfig);
}
}
};
byte[] data = zooKeeper.getData("/myconfig", watcher, null);
MyConfig originalMyConfig = objectMapper.readValue(new String(data), MyConfig.class);
log.info("原始数据: {}", originalMyConfig);
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
重点代码理解:
byte[] bytes = objectMapper.writeValueAsBytes(myConfig);
数据必须以字节的方式去传递
String s = zooKeeper.create("/myconfig", bytes, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
阻塞的方式创建节点
String s = zooKeeper.create("/myconfig", bytes, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
ZooDefs.Ids.OPEN_ACL_UNSAFE 相当于拥有所有权限
CreateMode.PERSISTENT 创建持久化节点
event.getType()== Event.EventType.NodeDataChanged && event.getPath()!=null && event.getPath().equals("/myconfig"
当前监听的事件为NodeDataChanged ,监听的几点路径为/myconfig
//将节点添加到监听中,写了两个地方,其中一个地方在通知完成后的地方,用于当通知发放后,还可以继续监听
byte[] data = zooKeeper.getData("/myconfig", this, null);
修改节点值
Stat stat = new Stat();
byte[] data = zooKeeper.getData(first_node, false, stat);
int version = stat.getVersion();
zooKeeper.setData(first_node, "third".getBytes(), 0);
//zooKeeper.setData(first_node, "third".getBytes(), version );
利用zooKeeper.getData(first_node, false, stat);来获取当前节点的start的详细信息;(l乐观锁)
根据当前版本号进行修改,如果版本信息不对的话,修改将不成功,防止代码并发的情况发生,在zk服务端中是不存在并发的情况的,其运行为阻塞队列式运行的。
删除节点值
跟修改节点一样操作,但是多了一个,如果传入的值为-1那么无视版本号删除;
getZooKeeper().delete("/config",-1);
异步请求
getZooKeeper().getData("/xsn", false, (rc, path, ctx, data, stat) -> {
Thread thread = Thread.currentThread();
log.info(" Thread Name: {}, rc:{}, path:{}, ctx:{}, data:{}, stat:{}",thread.getName(),rc, path, ctx, data, stat);},"xsn");
log.info(" over .");
执行结果
INFO [main:BaseOperations@81] - over .
INFO [main-EventThread:BaseOperations@79] - Thread Name: main-EventThread, rc:0, path:/xsn, ctx:xsn, data:[116, 101, 115, 116], stat:73,74,1609852651729,1609852662649,1,0,0,0,4,0,73
over在上面那个日志之后打印出来的
Curator
对zk支持最好的第三方客户端;封装方法便于使用加上maven即可使用
建立连接
CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient(connectString, retryPolicy);
监听
curatorFramework.getConnectionStateListenable().addListener(((client, newState) -> {
if (newState== ConnectionState.CONNECTED){
log.info(" 连接建立");
}
}));
创建节点
CuratorFramework curatorFramework = getCuratorFramework();
String path = curatorFramework.create().forPath("/curator‐node");
递归创建子节点
String pathWithParent = “/node-parent/sub-node-1”;
String path = curatorFramework.create().creatingParentsIfNeeded().forPath(pathWithParent);
获取数据
byte[] bytes = curatorFramework.getData().forPath("/curator‐node");
更新数据
curatorFramework.setData().forPath("/curator‐node",“changed!”.getBytes());
byte[] bytes = curatorFramework.getData().forPath("/curator‐node");
删除节点
curatorFramework.delete().guaranteed().deletingChildrenIfNeeded().forPath("/node‐parent");
防止异常的原因发生创建节点,在创建节点的时候先去判断一下这个节点是否已经存在了,不存在再创建
String forPath = curatorFramework
.create()
.withProtection()
.withMode(CreateMode.EPHEMERAL_SEQUENTIAL).
forPath("/curator-node", “some-data”.getBytes());
log.info(“curator create node :{} successfully.”, forPath);
zookeeper集群
Leader:负责写,读数据
Follower:负责读数据,如果Leader失联,他将参与选举,可能称为新的Leader
Observer:只负责读数据,不参与选举
leader写数据会把数据更新到follower上面,至少要更新过半以上的机子(包括本机leader)才会成功的返回给客户端,那么当机子数量多的时候,写的速度反而会比较低;所以推荐奇数台,因为在写的时候3台至少要写2台,四台却要写3台,所以一般推荐奇数台,因为3或者5都要比4总体来说好上不少。
安装zk集群:
我们在一台机子上安装zk的集群:
1、修改dataDir:各种日志之类的文件路径修改,独有
2、修改端口号,与其他集群节点区别开来
3、添加下面这一段,用于集群节点之间的通讯,server.1(这个1,2,3,4代表的式myid,每台服务的唯一标识,没有特别的含义)配置在
dataDir文件夹里面创建一个myid文件写上对应的1,2,3,4
2001,2002,2003,2004 用于集群之间的通讯的端口号
3001,3002,3003,3004用于集群选举使用的端口号
(如果在不同的机子上可以采用一样的端口号,因为他们的ip不同是可以区别开来的)
我们启动的时候没办法指定那一台机子是leader,这个只能靠zk内部的选举算法实现;选举算法会尽量让拥有最新数据的那台自己称为leader;Observer是可以指定的,就上上面那个2004:3004那台机子;
启动zk集群
./bin/zkServer.sh start conf/zoo-1.cfg(分别1234启动)
查看zk集群的分配
./bin/zkServer.sh status conf/zoo-1.cfg (也是1234查看)
连接zk集群,可以选择单台连接,也可以选择把所有ip和端口号全部输入进去,zk会随机选择一台进行连接;
如果选择单台连接:那么这台服务器挂掉,我们就和zk失去了连接,通过所有ip进行连接,那么有一半以上的zk还存活着就可以和zk保持连接;
./bin/zkCli.sh -server 192.168.88.3:2181,192.168.88.3:2182,192.168.88.3:2183,192.168.88.3:2184
官方自带的集群连接方法,我们优化后
byte[] data = getZooKeeper().getData("/zookeeper", false, stat);
官方自带的方法进行自动重连处理
while (true){
try {
Stat stat = new Stat();
byte[] data = getZooKeeper().getData("/zookeeper", false, stat);
log.info("get data : {}", new String(data));
TimeUnit.SECONDS.sleep(5);
}catch (Exception e){
e.printStackTrace();
log.info(" 开始重连......");
while (true){
log.info("zookeeper status :{}",getZooKeeper().getState().name());
if (getZooKeeper().getState().isConnected()) {
break;
}
TimeUnit.SECONDS.sleep(3);
}
}
}
Curator自动重连
RetryPolicy retryPolicy=new ExponentialBackoffRetry( 5*1000, 10 );
String connectStr = "192.168.88.3:2181,192.168.88.3:2182,192.168.88.3:2183,192.168.88.3:2184";
CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient(connectStr, retryPolicy);
curatorFramework.start();
String pathWithParent = "/test";
byte[] bytes = curatorFramework.getData().forPath(pathWithParent);
System.out.println(new String(bytes));
while (true) {
try {
byte[] bytes2 = curatorFramework.getData().forPath(pathWithParent);
System.out.println(new String(bytes2));
TimeUnit.SECONDS.sleep(5);
} catch (Exception e) {
e.printStackTrace();
}
}