安装比较简单,请参考之前的文章:https://www.yuque.com/tianyunperfect/ygzsw4/igckr3

定义:

  • 分布式、开源的协调框架
  • 文件系统+通知机制
  • Leader+N个Follower

基础

架构图

基于观察者模式的分布式框架
image.png

集群特点

image.png

  • Zookeeper:一个领导者Leader,多个跟随者Follower组成的集群。
  • 集群中只要有半数以上节点存活,Zookeeper集群就能正常服务。
  • 全局数据一致:每个Server保存一份相同的数据副本,Client无论连接到哪个Server,数据都是一致的。
  • 更新请求顺序进行,来自同一个Client的更新请求按其发送顺序依次执行。
  • 数据更新原子性,一次数据更新要么成功,要么失败。
  • 实时性,在一定时间范围内,Client能读到最新数据。

    数据结构

    整体上可以看作是一棵树, 每个节点称做一个ZNode。每一个ZNode默认能够存储1MB的数据, 每个ZNode都可以通过其路径唯一标识。
    image.png

    集群配置

    image.png

命令操作

image.png

应用场景

统一命名服务

在分布式环境下,经常需要对应用/服务进行统一命名,便于识别。
例如:IP不容易记住,而域名容易记住。
image.png

统一配置管理

可将配置信息写入ZooKeeper上的一个Znode,各个客户端服务器监听这个Znode
一旦Znode中的数据被修改, ZooKeeper将通知各个客户端服务器
image.png

统一集群管理

监控每个节点的状态
image.png

要点

选举机制

  • 半数机制
  • 一个Leader和N个Follower,启动的时候先选自己,没有达到半数则选id最大的。

image.png

节点类型

  • 持久(Persistent):客户端和服务器端断开连接后, 创建的节点不删除
  • 短暂(Ephemeral):客户端和服务器端断开连接后, 创建的节点自己删除(-e)
  • 还可以多一个选项:是否带编号(-s)
    1. create -s /sanguo "meinv"

监督原理

image.png

常见的监听:

  1. 监听节点数据的变化 get path [watch]
  2. 监听子节点增减的变化 ls path [watch]

    写流程

    image.png

    权限控制

参考:https://bboyjing.github.io/2016/09/12/Zookeeper%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E5%9B%9B%E3%80%90Java%E5%AE%A2%E6%88%B7%E7%AB%AFAPI%E4%B9%8B%E6%9D%83%E9%99%90%E6%8E%A7%E5%88%B6%E3%80%91/

ACL(Access Control List)访问控制列表

包括三个方面:

权限模式(Scheme)

(1)IP:从 IP 地址粒度进行权限控制

(2)Digest:最常用,用类似于 username:password 的权限标识来进行权限配置,便于区分不同应用来进行权限控制

(3)World:最开放的权限控制方式,是一种特殊的 digest 模式,只有一个权限标识“world:anyone”

(4)Super:超级用户

授权对象

授权对象指的是权限赋予的用户或一个指定实体,例如 IP 地址或是机器灯。

权限 Permission

(1)CREATE:数据节点创建权限,允许授权对象在该 Znode 下创建子节点

(2)DELETE:子节点删除权限,允许授权对象删除该数据节点的子节点

(3)READ:数据节点的读取权限,允许授权对象访问该数据节点并读取其数据内容或子节点列表等

(4)WRITE:数据节点更新权限,允许授权对象对该数据节点进行更新操作

(5)ADMIN:数据节点管理权限,允许授权对象对该数据节点进行 ACL 相关设置操作

Java操作Zookeeper

简单操作

  1. import lombok.SneakyThrows;
  2. import org.apache.zookeeper.*;
  3. import org.apache.zookeeper.data.Stat;
  4. import org.junit.Before;
  5. import org.junit.Test;
  6. import org.springframework.test.context.junit.jupiter.EnabledIf;
  7. import java.io.IOException;
  8. import java.util.List;
  9. /**
  10. * @Description
  11. * @Author 田云
  12. * @Date 2020/12/12 19:48
  13. * @Version 1.0
  14. */
  15. public class BaseTest {
  16. private String connectString = "127.0.0.1:2181";
  17. private int sessionTimeout = 2000;
  18. private ZooKeeper zooKeeper;
  19. @Before
  20. public void init() throws IOException {
  21. this.zooKeeper = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
  22. @SneakyThrows
  23. @Override
  24. public void process(WatchedEvent event) {
  25. getChildrenAndWatch();
  26. }
  27. });
  28. }
  29. /**
  30. * 创建节点
  31. *
  32. * @throws KeeperException
  33. * @throws InterruptedException 中断异常
  34. */
  35. @Test
  36. public void createNode() throws KeeperException, InterruptedException {
  37. String path = this.zooKeeper.create("/alvin",
  38. "为学者日益".getBytes(),
  39. ZooDefs.Ids.OPEN_ACL_UNSAFE,
  40. CreateMode.PERSISTENT);
  41. System.out.println(path);
  42. }
  43. @Test
  44. public void getChildrenAndWatch() throws KeeperException, InterruptedException {
  45. System.out.println("---start---");
  46. List<String> children = zooKeeper.getChildren("/", true);
  47. children.forEach(System.out::println);
  48. System.out.println("---end---");
  49. }
  50. @Test
  51. public void longSleep() throws InterruptedException, KeeperException {
  52. getChildrenAndWatch();
  53. Thread.sleep(Long.MAX_VALUE);
  54. }
  55. /**
  56. * 节点是否存在
  57. *
  58. * @throws KeeperException
  59. * @throws InterruptedException 中断异常
  60. */
  61. @Test
  62. public void nodeExist() throws KeeperException, InterruptedException {
  63. Stat exists = zooKeeper.exists("/test", false);
  64. System.out.println(exists);
  65. }
  66. }

服务器节点动态上下线的案例

  • 服务端:连接、注册 ``` import org.apache.zookeeper.*;

import java.io.IOException; import java.util.concurrent.ThreadLocalRandom;

/**

  • 分发服务器 *
  • @Description
  • @Author 田云
  • @Date 2021/3/20 10:44
  • @Version 1.0 */ public class DistributeServer { private static String connectString = “127.0.0.1:2181”; private static int sessionTimeout = 2000; private ZooKeeper zkClient = null;

    private String parentNode = “/servers”;

    // 创建到 zk 的客户端连接 public void getConnect() throws IOException {

    1. zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
    2. @Override
    3. public void process(WatchedEvent event) {
    4. }
    5. });

    }

    // 注册服务器 public void registServer(String hostname) throws Exception {

    1. String create = zkClient.create(
    2. parentNode + "/server",
    3. hostname.getBytes(),
    4. ZooDefs.Ids.OPEN_ACL_UNSAFE,
    5. CreateMode.EPHEMERAL_SEQUENTIAL);
    6. System.out.println(hostname + " is online " + create);

    }

    // 业务功能 public void business(String hostname) throws Exception {

    1. System.out.println(hostname + " is working ...");
    2. Thread.sleep(Long.MAX_VALUE);

    }

    public static void main(String[] args) throws Exception {

    1. // 1 获取 zk 连接
    2. DistributeServer server = new DistributeServer();
    3. server.getConnect();
    4. // 2 利用 zk 连接注册服务器信息
    5. server.registServer(args[0]);
    6. // 3 启动业务功能
    7. server.business(args[0]);

    } } ```

  • 客户端:连接、监听 ``` import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper;

import java.io.IOException; import java.util.ArrayList; import java.util.List;

/**

  • @Description
  • @Author 田云
  • @Date 2021/3/20 10:50
  • @Version 1.0 */ public class DistributeClient { private static String connectString = “127.0.0.1:2181”; private static int sessionTimeout = 2000; private ZooKeeper zkClient = null;

    private String parentNode = “/servers”;

    // 创建到 zkClient 的客户端连接 public void getConnect() throws IOException {

    1. zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
    2. @Override
    3. public void process(WatchedEvent event) {
    4. // 再次启动监听
    5. try {
    6. getServerList();
    7. } catch (Exception e) {
    8. e.printStackTrace();
    9. }
    10. }
    11. });

    }

    // 获取服务器列表信息 public void getServerList() throws Exception {

    1. // 1 获取服务器子节点信息,并且对父节点进行监听
    2. List<String> children = zkClient.getChildren(parentNode, true);
    3. // 2 存储服务器信息列表
    4. ArrayList<String> servers = new ArrayList<>();
    5. // 3 遍历所有节点,获取节点中的主机名称信息
    6. for (String child : children) {
    7. byte[] data = zkClient.getData(parentNode + "/" + child, false, null);
    8. servers.add(new String(data));
    9. }
    10. // 4 打印服务器列表信息
    11. System.out.println(servers);

    }

    // 业务功能,为了让程序存在 public void business() throws Exception {

    1. System.out.println("client is working ...");
    2. Thread.sleep(Long.MAX_VALUE);

    }

    public static void main(String[] args) throws Exception {

    1. // 1 获取 zk 连接
    2. DistributeClient client = new DistributeClient();
    3. client.getConnect();
    4. // 2 获取 servers 的子节点信息,从中获取服务器信息列表
    5. client.getServerList();
    6. // 3 业务进程启动
    7. client.business();

    } } ```

dubbo的provider就是一个目录

ls /dubbo/com.alvin.service.UserService/providers

  1. [dubbo://192.168.124.6:20880/com.alvin.service.UserService?anyhost=true&application=xxx-provider&bean.name=ServiceBean:com.alvin.service.UserService&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=com.alvin.service.UserService&methods=getUser&pid=38490&register=true&release=2.7.3&side=provider&timestamp=1616205719481, dubbo://192.168.124.6:20881/com.alvin.service.UserService?anyhost=true&application=xxx-provider&bean.name=ServiceBean:com.alvin.service.UserService&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=com.alvin.service.UserService&methods=getUser&pid=38522&register=true&release=2.7.3&side=provider&timestamp=1616205720860]