【弄nèng - Zookeeper】Zookeeper入门教程(五)—— Master选举(Curator-recipes)

本文介绍zk客户端curator的使用,本文主要介绍Curator recipes的使用
官方文档传送门
官方文档Curator recipes
参考:http://www.throwable.club/2018/12/16/zookeeper-curator-usage/
https://blog.csdn.net/hosaos/article/details/88727817
参考书籍:《从Paxos到ZooKeeper 分布式一致性原理与实践》

1. Master选举

再多台机器中选择一个Master,利用ZK的特性。多台机器同时向一个节点创建子节点,最终只会有一个成功,那么他就是Master。有两种实现方式:LeaderSelector和LeaderLatch

pom

		<dependency>
			<groupId>org.apache.curator</groupId>
			<artifactId>curator-recipes</artifactId>
			<version>2.13.0</version>
		</dependency>

2. LeaderLatch

原理
指定一个根路径,例如/leader_latch,多个机器同时向该根路径下创建临时顺序节点,/leader_latch/node_1/leader_latch/node_2/leader_latch/node_3,,/leader_latch/node_4,编号最小node_1对应的客户端成为leader,其他节点都监听前一个节点的删除事件,在前一个节点删除后进行重新抢主。

关键方法

// 开始抢主
void start()
// 释放leader权限
void close()
// 阻塞线程,尝试获取leader权限,但不一定成功,超时失败
boolean await(long, java.util.concurrent.TimeUnit)
// 判断是否拥有leader权限
boolean hasLeadership()

事例

import com.google.common.collect.Lists;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.leader.LeaderLatch;
import org.apache.curator.framework.recipes.leader.LeaderSelector;
import org.apache.curator.framework.recipes.leader.LeaderSelectorListenerAdapter;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;

/**
 * Master选择
 */

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class MasterApiTests {

    private CuratorFramework client;

    /**
     * 使用Fluent风格API创建
     */
    @Before
    public void client() {
        // 重试策略
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
        client = CuratorFrameworkFactory.builder()
                .connectString("localhost:2181")
                .sessionTimeoutMs(5000)  // 会话超时时间,默认60000ms
                .connectionTimeoutMs(10000) // 连接超时时间,默认15000ms
                .retryPolicy(retryPolicy) // 重试策略
                .namespace("base") // 名称空间,如果这里设置了名称空间,那么所有路径都将预先指定名称空间
                .build();
        client.start();
    }

    /**
     * LeaderLatch
     */
    @Test
    public void LeaderLatch() throws Exception {
        // 客户端数
        int CLINET_COUNT = 10;
        String path = "/latch-path";

        List<CuratorFramework> clientsList = Lists.newArrayListWithCapacity(CLINET_COUNT);
        List<LeaderLatch> latchList = Lists.newArrayListWithCapacity(CLINET_COUNT);
        // 创建10个zk客户端模拟leader选举
        for (int i = 0; i < CLINET_COUNT; i++) {
            CuratorFramework client = getZkClient();
            clientsList.add(client);
            // 参数3:客户端ID
            // 参数4:关闭策略,SILENT-关闭时不触发监听器回调,NOTIFY_LEADER-关闭时触发监听器回调方法,默认不触发
            LeaderLatch leaderLatch = new LeaderLatch(client, path, "CLIENT_" + i);
            latchList.add(leaderLatch);
            // 调用start方法开始抢主
            leaderLatch.start();
        }
        // 判断当前leader是哪个客户端
        checkLeader(latchList);

    }

    private void checkLeader(List<LeaderLatch> leaderLatchList) throws Exception {
        // 睡5秒
        Thread.sleep(5000);
        for (LeaderLatch leaderLatch : leaderLatchList) {
            // 判断当前客户端是否是leader
            if (leaderLatch.hasLeadership()) {
                System.out.println("当前leader:" + leaderLatch.getId());
                // 释放leader权限
                leaderLatch.close();
                // 检查当前leader
                checkLeader(leaderLatchList);
            }
        }
    }

    private CuratorFramework getZkClient() {
        ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 3, 5000);
        CuratorFramework zkClient = CuratorFrameworkFactory.builder()
                .connectString("localhost:2181")
                .sessionTimeoutMs(5000)  // 会话超时时间,默认60000ms
                .connectionTimeoutMs(10000) // 连接超时时间,默认15000ms
                .retryPolicy(retryPolicy) // 重试策略
                .build();
        zkClient.start();
        return zkClient;
    }

}

控制台
在这里插入图片描述

3. LeaderSelector

原理
利用Curator中InterProcessMutex分布式锁进行抢主,抢到锁的的为Leader。

关键方法

// 开始抢主
void start()
// 在释放leader权限后,自动加入抢主队列,重新抢主
void autoRequeue()

事例

import com.google.common.collect.Lists;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.leader.LeaderLatch;
import org.apache.curator.framework.recipes.leader.LeaderSelector;
import org.apache.curator.framework.recipes.leader.LeaderSelectorListenerAdapter;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;

/**
 * Master选择
 */

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class MasterApiTests {

    private CuratorFramework client;

    /**
     * 使用Fluent风格API创建
     */
    @Before
    public void client() {
        // 重试策略
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
        client = CuratorFrameworkFactory.builder()
                .connectString("localhost:2181")
                .sessionTimeoutMs(5000)  // 会话超时时间,默认60000ms
                .connectionTimeoutMs(10000) // 连接超时时间,默认15000ms
                .retryPolicy(retryPolicy) // 重试策略
                .namespace("base") // 名称空间,如果这里设置了名称空间,那么所有路径都将预先指定名称空间
                .build();
        client.start();
    }

    /**
     * LeaderSelector
     */
    @Test
    public void LeaderSelector() throws Exception {
        String path = "/master-path";
        int CLINET_COUNT = 10;
        List<CuratorFramework> clientsList = Lists.newArrayListWithCapacity(CLINET_COUNT);
        List<LeaderSelector> latchList = Lists.newArrayListWithCapacity(CLINET_COUNT);

        // 创建10个zk客户端模拟leader选举
        for (int i = 0; i < CLINET_COUNT; i++) {
            CuratorFramework client = getZkClient();
            clientsList.add(client);
            // 可以多个参数3:ExecutorService: Master选举单独使用的线程池
            // public LeaderSelector(CuratorFramework client, String leaderPath, ExecutorService executorService, LeaderSelectorListener listener)
            LeaderSelector selector = new LeaderSelector(client, path,
                    new LeaderSelectorListenerAdapter() {
                        public void takeLeadership(CuratorFramework client) throws Exception {
                            System.out.println(Thread.currentThread().getName() + "成为了Master");
                            Thread.sleep(3000);
                            System.out.println("释放Master权利");
                        }
                    });
            latchList.add(selector);
        }


        latchList.forEach(leaderSelector -> {
            // 重新获得竞争权利
            leaderSelector.autoRequeue();
            // 启动选举
            leaderSelector.start();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread.sleep(Integer.MAX_VALUE);
    }

    private CuratorFramework getZkClient() {
        ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 3, 5000);
        CuratorFramework zkClient = CuratorFrameworkFactory.builder()
                .connectString("localhost:2181")
                .sessionTimeoutMs(5000)  // 会话超时时间,默认60000ms
                .connectionTimeoutMs(10000) // 连接超时时间,默认15000ms
                .retryPolicy(retryPolicy) // 重试策略
                .build();
        zkClient.start();
        return zkClient;
    }

}

控制台
在这里插入图片描述

4. 区别

  1. 实现原理略有不同,不过Curator中InterProcessMutex分布式锁的实现原理同LeaderLatch非常相似。
  2. LeaderSelector处理完业务逻辑后会自动释放锁,并且可以重新获取。LeaderLatch获取一次就不能再获取了。

源码地址

IT-CLOUD-ZOOKEEPER :zookeeper客户端Curator事例


项目推荐

IT-CLOUD :IT服务管理平台,集成基础服务,中间件服务,监控告警服务等。
IT-CLOUD-ACTIVITI6 :Activiti教程源码。博文在本CSDN Activiti系列中。
IT-CLOUD-ELASTICSEARCH :elasticsearch教程源码。博文在本CSDN elasticsearch系列中。
IT-CLOUD-KAFKA :spring整合kafka教程源码。博文在本CSDN kafka系列中。
IT-CLOUD-KAFKA-CLIENT :kafka client教程源码。博文在本CSDN kafka系列中。
IT-CLOUD-ZOOKEEPER :zookeeper客户端Curator事例。博文在本CSDN zookeeper系列中。

开源项目,持续更新中,喜欢请 Star~

发布了178 篇原创文章 · 获赞 48 · 访问量 22万+

猜你喜欢

转载自blog.csdn.net/yy756127197/article/details/105262282