分布式框架Zookeeper(二)-就怕你还看不懂-Zookeeper客户端使用、Zookeeper集群模式

Zookeeper Java 客户端

项目构建

Zookeeper官方客户端没有和服务端代码分离,他们为同一个Jar文件,我们直接引入Zookeeper的maven依赖就可以了,这里版本要保持与服务端一直,不然会有很多很多兼容性的问题

		<dependency>
			<groupId>org.apache.zookeeper</groupId>
			<artifactId>zookeeper</artifactId>
			<version>3.5.8</version>
		</dependency>

创建客户端实例

@Slf4j
public class ConfigCenter {
    private static ZooKeeper zooKeeper=null;
    private final static String SERVER_ADD="127.0.0.1:2181";
    private final static int SESSION_TIMEOUT=10*60*60;
    private final static CountDownLatch countDownLatch=new CountDownLatch(1);

    public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
        zooKeeper=new ZooKeeper(SERVER_ADD, SESSION_TIMEOUT, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                if (watchedEvent.getType().equals(Event.EventType.None)&&
                       watchedEvent.getState().equals(Event.KeeperState.SyncConnected)){
                    log.info("connected");
                    countDownLatch.countDown();
                }
            }
        });
        countDownLatch.await();
    }

zookeeper构造方法

public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher) throws IOException {
        this(connectString, sessionTimeout, watcher, false);
    }

    public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, ZKClientConfig conf) throws IOException {
        this(connectString, sessionTimeout, watcher, false, conf);
    }

 **加粗样式**   public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly, HostProvider aHostProvider) throws IOException {
        this(connectString, sessionTimeout, watcher, canBeReadOnly, aHostProvider, (ZKClientConfig)null);
    }

    public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly, HostProvider aHostProvider, ZKClientConfig clientConfig) throws IOException {
//...省略无关代码
        this.cnxn.start();
    }
  • connectStringZookeeper服务端的地址,由IP:port组成;当有多台Zookeeper机器,则可以使用host1:port1,host2:port2,host3,port3来指定。
  • 另外,也可以在connectString中设置客户端连接上ZooKeeper 后的根目录,方法是在host:port字符串之后添加上这个根目录,例如,host1:port1,host2:port2,host3:port3/zk-base,**这样就指定了该客户端连 接上ZooKeeper服务器之后,所有对ZooKeeper 的操作,都会基于这个根目录。**例如,客户端对/sub-node 的操作,最终创建 /zk-node/sub-node, 这个目录也叫Chroot,即客户端隔离命名空间。
  • sessionTimeout:会话超时时间,超过一定时间没有发送心跳给服务端,服务端认为客户端失活,会话失效会;
  • watcher:ZooKeeper允许 客户端在构造方法中传入一个接口 watcher (org.apache. zookeeper. Watcher)的实现类对象来作为默认的 Watcher事件通知处理器。当然,该参 数可以设置为null 以表明不需要设置默认的 Watcher处理器。
  • canBeReadOnly:用于表示当前会员是否支持只读模式。默认情况下,zk集群中,一个机器如果和集群中过半以上机器失去网络连接,那么这个机器将不再处理客户端请求(包括读写请求)。但是在某些场景下,当Zookeeper服务器发生此类故障时,还是希望服务器能够提供读服务(写无法提供)

创建结点

   public void createPZonde() throws KeeperException, InterruptedException {
        String path = zooKeeper.create(ZK_NODE, "pdata".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, PERSISTENT);
        log.info(path);
    }

在这里插入图片描述
其他的类型结点:指定最后一个参数即可:

public enum CreateMode {
    PERSISTENT(0, false, false, false, false),
    PERSISTENT_SEQUENTIAL(2, false, true, false, false),
    EPHEMERAL(1, true, false, false, false),
    EPHEMERAL_SEQUENTIAL(3, true, true, false, false),
    CONTAINER(4, false, false, true, false),
    PERSISTENT_WITH_TTL(5, false, false, false, true),
    PERSISTENT_SEQUENTIAL_WITH_TTL(6, false, true, false, true);
}

修改节点

    public static void setPZnode() throws KeeperException, InterruptedException {
        Stat stat = new Stat();
        byte[] data = zooKeeper.getData(ZK_NODE, false, stat);
        log.info("修改前: {}",new String(data));
        zooKeeper.setData(ZK_NODE,"changed".getBytes(),stat.getVersion());
        byte[] newData=zooKeeper.getData(ZK_NODE,false,stat);
        log.info("修改前: {}",new String(newData));
    }

在这里插入图片描述

删除结点

  private static void deleteNode() throws KeeperException, InterruptedException {
        Stat stat = new Stat();
        byte[] data = zooKeeper.getData(ZK_NODE, false, stat);
        log.info("删除前: {}",new String(data));
        zooKeeper.delete(ZK_NODE,stat.getVersion());
    }

在这里插入图片描述
在这里插入图片描述
确实不存在zknode2结点。

Curator

Curator 是 ZooKeeper 客户端框架,Curator项目是现在ZooKeeper 客户端中使用最多,对ZooKeeper 版本支持最好的第三方客户端,并推荐使用,Curator 把我们平时常用的很多 ZooKeeper 服务开发功能做了封装,例如 Leader 选举、 分布式计数器、分布式锁。这就减少了技术人员在使用 ZooKeeper 时的大部分底层细节开发工 作。在会话重新连接、Watch 反复注册、多种异常处理等使用场景中,用原生的 ZooKeeper 处理比较复杂。而在使用 Curator 时,由于其对这些功能都做了高度的封装,使用起来更加简单,不但减少了开发时间,而且增强了程序的可靠性。

Curator实战

引入Curator框架依赖,一个是curator-framework,封装了一些底层API,另一个是curator-recipes包,该包封装了一些 ZooKeeper 服务的高级特性,如: Cache 事件监听、选举、分布式锁、分布式 Barrier。

<!-- -->
		<dependency>
			<groupId>org.apache.curator</groupId>
			<artifactId>curator-recipes</artifactId>
			<version>5.0.0</version>
			<exclusions>
				<exclusion>
					<groupId>org.apache.zookeeper</groupId>
					<artifactId>zookeeper</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.apache.curator</groupId>
			<artifactId>curator-x-discovery</artifactId>
			<version>5.0.0</version>
			<exclusions>
				<exclusion>
					<groupId>org.apache.zookeeper</groupId>
					<artifactId>zookeeper</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.apache.zookeeper</groupId>
			<artifactId>zookeeper</artifactId>
			<version>3.5.8</version>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.12</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.8.3</version>
		</dependency>
	</dependencies>

会话创建

要进行客户端服务器交互,第一步就要创建会话 Curator 提供了多种方式创建会话,比如用静态工厂方式创建:

 

    private static void createClient1() {
        ExponentialBackoffRetry retry = new ExponentialBackoffRetry(1000, 3);
        CuratorFramework client = CuratorFrameworkFactory.newClient(connectAdd, retry);
        client.start();
    }

或者流式风格创建:

   private static void createClient2() {
        ExponentialBackoffRetry retry = new ExponentialBackoffRetry(1000, 3);
         CuratorFramework client = CuratorFrameworkFactory.builder().connectString(connectAdd).canBeReadOnly(false)
                .retryPolicy(retry).sessionTimeoutMs(5000).namespace("base").connectionTimeoutMs(5000).build();
        client.start();

    }

参数如下:

  • connectString
    服务器地址列表,在指定服务器地址列表的时候可以是一个地址,也可以 是多个地址。如果是多个地址,那么每个服务器地址列表用逗号分隔, 如 host1:port1,host2:port2,host3;port3 。

  • canBeReadOnly:只支持只读模式

  • sessionTimeoutMs:会话超时时间,作用在客户端

  • connectionTimeoutMs:客户端创建会话的超时时间,用来限制客户端发起一个会话连接到接收 ZooKeeper 服务端应答的时间。

  • namespace:创建客户端时创建一个只属于自己的目录,这里是base;

  • retryPolicy:重试策略,当客户端异常退出或者与服务端失去连接的时候,可以通过设置客户端重新连接Zookeeper服务端,而Curator提供了一次重试,多次重试等不同种类的实现方式。在Curator内部,可以通过服务器返回的KeeperException的状态代码判断是否进行重试处理,如果返回的是OK,表示一切操作都没有问题,而SYSTEMERROR表示系统或者服务端错误;

策略名称描述
ExponentialBackoffRetry重试一组次数,重试之间的睡眠时间增加
RetryNTimes重试最大次数
RetryOneTime重试一次
RetryUntilElapsed在给定的时间结束之前重试

创建节点

  private static void createNode() throws Exception {
        String p = client.create().withMode(CreateMode.EPHEMERAL).forPath("/curator-node","curator-data".getBytes());
        log.info("curator create node :{} successfully.",p);
    }

使用 create 函数创建数据节点,并通过 withMode 函数指定节点类型 (持久化节点,临时节点,顺序节点,临时顺序节点,持久化顺序节点等),默认是持久化节 点,之后调用 forPath 函数来指定节点的路径和数据信息。
在这里插入图片描述

一次性创建带层级结构的节点

   private static void createWithParent() throws Exception {
        String pathWithP="/node-parent/sub1";
        String path = client.create().creatingParentsIfNeeded().forPath(pathWithP);
        log.info("curator create node :{} successfully.",path);
    }

在这里插入图片描述
在这里插入图片描述

获取数据

  public static Object getData() throws Exception {
        byte[] bytes = client.getData().forPath("/curator-node");
        System.out.println("------------------------------------");
        System.out.println(new String(bytes));
        return bytes;
    }

在这里插入图片描述

更新节点

    private static void udpateData() throws Exception {
        Stat path = client.setData().forPath("/curator-node", "changed".getBytes());
        byte[] bytes = client.getData().forPath("/curator-node");
        log.info("get data from node /curator‐node :{} successfully.",new String(bytes));
    }

在这里插入图片描述

删除节点

   private static void deleteNode() throws Exception {
        client.delete().guaranteed().deletingChildrenIfNeeded().forPath("/curator-node");
        System.out.println("delete ok");
    }

在这里插入图片描述
在这里插入图片描述

  • guaranteed:该函数的功能如字面意思一样,主要起到一个保障删除成功的作用,其底层工作 方式是:只要该客户端的会话有效,就会在后台持续发起删除请求,直到该数据节点在 ZooKeeper 服务端被删除。
  • deletingChildrenIfNeeded:指定了该函数后,系统在删除该数据节点的时候会以递归的方式 直接删除其子节点,以及子节点的子节点

异步处理

public interface BackgroundCallback {
    void processResult(CuratorFramework var1, CuratorEvent var2) throws Exception;
}
  • 用来处理服务器端返回来的信息,这个处理过程是 在异步线程中调用,默认在 EventThread 中调用,也可以自定义线程池
  • 主要参数为 client 客户端, 和 服务端事件 event
  • inBackground 异步处理默认在EventThread中执行
 private static void callback() throws Exception {
        byte[] bytes = client.getData().inBackground((item1, item2) -> {
            log.info(" *********************************************background: {}", item2);
        }).forPath("/curator-node");
    }

在这里插入图片描述

指定线程池
   private static void callbackWithExecutor() throws Exception {
        ExecutorService service = Executors.newFixedThreadPool(2);
        client.getData().inBackground((item1,item2)->{
            log.info(" *********************************************background: {}", item2);
        },service).forPath("/curator-node");
    }

在这里插入图片描述

Curator监听器

public interface CuratorListener {
    void eventReceived(CuratorFramework var1, CuratorEvent var2) throws Exception;
}

针对 background 通知和错误通知。使用此监听器之后,调用inBackground 方法会异步获得 监听

Curator Caches

Curator 引入了 Cache 来实现对 Zookeeper 服务端事件监听,Cache 事件监听可以理解为一 个本地缓存视图与远程 Zookeeper 视图的对比过程。Cache 提供了反复注册的功能。Cache 分 为两类注册类型:节点监听和子节点监听。

    private static void addListener() throws Exception {
        NodeCache cache = new NodeCache(client, CuratorTest.nodeCache);
        cache.getListenable().addListener(new NodeCacheListener() {
            @Override
            public void nodeChanged() throws Exception {
                log.info("{} path nodeChanged: ",nodeCache);
                printNodeData();
            }
        });
        cache.start();
    }

path cache

PathChildrenCache 会对子节点进行监听,但是不会对二级子节点进行监听.
可以通过注册监听器来实现,对当前节点的子节点数据变化的处理

    private static void addPathListener() throws Exception {
        PathChildrenCache pathChildrenCache=new PathChildrenCache(client,PATH,true);
        pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
            @Override
            public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {
                log.info("event: {}",pathChildrenCacheEvent);
            }
        });
        pathChildrenCache.start();
    }

Zookeeper集群模式安装

Linux下的集群搭建

本例搭建的是伪集群模式,即一台机器上启动四个zookeeper实例组成集群,真正的集群模式无 非就是实例IP地址不同,搭建方法没有区别

  1. 配置JAVA环境,检验环境:保证是jdk7 及以上即可
java ‐version
  1. 下载并解压zookeeper
wget https://mirror.bit.edu.cn/apache/zookeeper/zookeeper‐3.5.8/apache‐zookeep er‐3.5.8‐bin.tar.gz
 tar ‐zxvf apache‐zookeeper‐3.5.8‐bin.tar.gz
 cd apache‐zookeeper‐3.5.8‐bin
  1. 重命名zoo_sample.cfg文件
cp conf/zoo_sample.cfg conf/zoo‐1.cfg
  1. 修改配置文件zoo-1.cfg,原配置文件里有的,修改成下面的值,没有的则加上
# The number of milliseconds of each tick
# 用于配置Zookeeper中最小时间单位的长度,很多运行时的时间间隔都是 使用#tickTime的倍数来表示的。
tickTime=2000
# 该参数用于配置Leader服务器等待Follower启动,并完成数据同步的时间。
#Follower服务器再启动过程中,会与Leader建立连接并完成数据的同步,从而确定自己对外提供服务的起始状态。
#Leader服务器允许Follower再initLimit 时间内完成这个工 作
initLimit=10
# The number of ticks thatcan pass between 
# sending a request and getting an acknowledgement
#LeaderFollower心跳检测的最大延时时间
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just 
# example sakes.
dataDir=E:/java_springboot/Java_Zookeeper/data-1
# the port at which the clients will connect
clientPort=2181

server.1=127.0.0.1:2001:3001:participant
server.2=127.0.0.1:2002:3002:participant
server.3=127.0.0.1:2003:3003:participant
server.4=127.0.0.1:2004:3004:observer
  • server.A=B:C:D:E
    A是一个数字,表示第几号服务器,B是IP地址,C是服务器与leader交换信息的端口,D是leader挂了,需要一个端口来重新进行选举,选出一个新的leader,这个端口就是用来执行选举时服务器相互通信的端口。
    如果是伪集群的配 置方式,由于 B 都是一样,所以不同的 Zookeeper 实例通信端口号不能一样,所以要给 它们分配不同的端口号。如果需要通过添加不参与集群选举以及事务请求的过半机制的 Observer节点,可以在E的位置,添加observer标识
  1. 再从zoo-1.cfg复制三个配置文件zoo-2.cfg,zoo-3.cfg和zoo-4.cfg,只需修改 dataDir和clientPort不同即可
cp conf/zoo1.cfg conf/zoo2.cfg 
cp conf/zoo1.cfg conf/zoo3.cfg 
cp conf/zoo1.cfg conf/zoo4.cfg

vim conf/zoo2.cfg
dataDir=/usr/local/data/zookeeper2
clientPort=2182

vim conf/zoo3.cfg
dataDir=/usr/local/data/zookeeper3
clientPort=2183

vim conf/zoo4.cfg
dataDir=/usr/local/data/zookeeper4
clientPort=2184


  1. 标识Server ID 创建四个文件夹/usr/local/data/zookeeper-1,/usr/local/data/zookeeper- 2,/usr/local/data/zookeeper-3,/usr/local/data/zookeeper-4,在每个目录中创建文件 myid 文件,写入当前实例的server id,即1,2,3,4
cd /usr/local/data/zookeeper‐1 
vim myid 
1

cd /usr/local/data/zookeeper‐2
vim myid 
2

cd /usr/local/data/zookeeper‐3
vim myid 
3
cd /usr/local/data/zookeeper‐4
vim myid 
4
  1. 启动三个zookeeper实例
bin/zkServer.sh start conf/zoo1.cfg
bin/zkServer.sh start conf/zoo2.cfg
bin/zkServer.sh start conf/zoo3.cfg
  1. 检测集群状态,也可以直接用命令 zkServer.sh status conf/zoo1.cfg 进行每台服务 的状态查询

在这里插入图片描述

Window环境下的搭建

请看这篇文章,传递门,我亲手搭建成功了。

在这里插入图片描述
运行客户端:
在这里插入图片描述
连接得上,通过config查看配置。

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值