PS : 文中的所有案例,均来自下面参考文章中的例子。
我只是在上面加了一点点结合公司业务场景的理解

言归正传,直接进入整体。

1、统一的配置管理

前记:个人理解,原则上说,只要可以实现 k-v 存储的,都可以用来做统一的配置中心。
你甚至可以使用redis 作为配置中心的管理。
对比之下,我还是推荐使用针对配置更为专业的 Apollo 来管理。

先不跑偏,回到我们的主题,我们结合 Zookeeper 来实现一个配置中心。

1.1 配置特性

在此之前,我们先认为下配置管理中心一般会有哪些特性:

  1. 数据量很小的 k-v
  2. 配置信息会在程序运行期间发生动态变化
  3. 集群之间共享,配置一致

而 Zookeeper 的 Znode 中的 临时节点刚好可以满足上面的条件要求。

1.2 Zookeeper 的方案特性

方案设计:
v2-edfda7e04b46c3f214386146b4118bdb_hd.jpg

说明:
ZooKeeper 采用的是推拉结合的方式。

  1. : 服务端会推给注册了监控节点的客户端 Wathcer 事件通知
  2. : 客户端获得通知后,然后主动到服务端拉取最新的数据

1.3 案例

比如我们现在有三个系统A、B、C,他们有三份配置,分别是ASystem.yml、BSystem.yml、CSystem.yml,然后,这三份配置又非常类似,很多的配置项几乎都一样。

  • 此时,如果我们要改变其中一份配置项的信息,很可能其他两份都要改。并且,改变了配置项的信息很可能就要重启系统

于是,我们希望把ASystem.yml、BSystem.yml、CSystem.yml相同的配置项抽取出来成一份公用的配置common.yml,并且即便common.yml改了,也不需要系统A、B、C重启。

image.png

做法:我们可以将common.yml这份配置放在ZooKeeper的Znode节点中,系统A、B、C监听着这个Znode节点有无变更,如果变更了,及时响应。
image.png

推广 :

日常的开发中,比如我们的 PHP 对应的 config 文件.
同样的还有 DB 文件,在开发、测试、预发布、生产各自的配置文件不同。可以通过配置中心统一管理。

参考 Apollo

image.png

2、统一命名服务

2.1 分解

先结合域名的例子,做个讲解。

比如说,现在我有一个域名www.java3y.com,但我这个域名下有多台机器:

  • 192.168.1.1
  • 192.168.1.2
  • 192.168.1.3
  • 192.168.1.4

别人访问www.java3y.com即可访问到我的机器,而不是通过IP去访问。
同时在这期间,集群中的任何一个IP对应的服务器,都有可能发生故障等失去连接。

image.png

PS : 统一的场景,类似这种负载均衡的分配的。我们公司目前使用的结合 consul + HA + Nginx

2.2 案例

服务的提供方,注册到Consul 。

image.png

当用户通过浏览器访问 https://ld.xxx.com/v2/oss_corp

nginx 会分解upstream 选择对应的一个服务

可以参考内网访问地址 : http://192.168.1.193/upstream_list

image.png

根据算法,会选择consul 的注册上的服务一个,转发到对应的服务器上。

同时,当那个服务器节点,断线之后,consul 会自动发现更新配置。新来的请求,不会转发到对应的服务节点上。

负载均衡是一种手段,用来把对某种资源的访问分摊给不同的设备,从而减轻单点的压力。

实现的思路:

  1. 首先建立 Servers 节点,并建立监听器监视 Servers 子节点的状态(用于在服务器增添时及时同步当前集群中服务器列表)
  2. 在每个服务器启动时,在 Servers 节点下建立临时子节点 Worker Server,并在对应的字节点下存入服务器的相关信息,包括服务的地址,IP,端口等等
  3. 可以自定义一个负载均衡算法,在每个请求过来时从 ZooKeeper 服务器中获取当前集群服务器列表,根据算法选出其中一个服务器来处理请求

3、服务注册和服务发现

使用场景不同,但是原理大同小异。下面的我就简单论述了。(和上面的例子差不多)

对应的模型:

image-20191120102121890.png

步骤:

  1. 将产品服务的信息注册到zookeeper的节点上
  2. 然后获取到节点上的信息并存储起来(本文存到List)
  3. Watcher机制监控List里数据的变化并更新数据 (假如产品服务2挂了通过监听机制将其移出)
  4. 利用轮询或者hash等算法去获取List里的数据供订单服务调用(负载均衡)

映射图如下:

image-20191120103728093.png

4、分布式锁

系统A、B、C 都去访问/locks节点

image-20191120095650446.png

访问的时候会创建带顺序号的临时/短暂(EPHEMERAL_SEQUENTIAL)节点,比如,系统A创建了id_000000节点,系统B创建了id_000002节点,系统C创建了id_000001节点。

image-20191120095711496.png

接着,拿到/locks节点下的所有子节点(id_000000,id_000001,id_000002),判断自己创建的是不是最小的那个节点

  • 如果是,则拿到锁。

    • 释放锁:执行完操作后,把创建的节点给删掉
  • 如果不是,则监听比自己要小1的节点变化

举个例子:

  • 系统A拿到/locks节点下的所有子节点,经过比较,发现自己(id_000000),是所有子节点最小的。所以得到锁
  • 系统B拿到/locks节点下的所有子节点,经过比较,发现自己(id_000002),不是所有子节点最小的。所以监听比自己小1的节点id_000001的状态
  • 系统C拿到/locks节点下的所有子节点,经过比较,发现自己(id_000001),不是所有子节点最小的。所以监听比自己小1的节点id_000000的状态
  • …...
  • 等到系统A执行完操作以后,将自己创建的节点删除(id_000000)。通过监听,系统C发现id_000000节点已经删除了,发现自己已经是最小的节点了,于是顺利拿到锁
  • ….系统B如上
解释的真的是太好了

5、FIFO队列

使用 ZooKeeper 实现 FIFO 队列,
入队操作就是在queue_fifo下创建自增序的子节点,并把数据(队列大小)放入节点内。
出队操作就是先找到queue_fifo下序号最下的那个节点,取出数据,然后删除此节点。

/queue_fifo
|
├── /host1-000000001
├── /host2-000000002
├── /host3-000000003
└── /host4-000000004

创建完节点后,根据以下步骤确定执行顺序:

  1. 通过get_children()接口获取/queue_fifo节点下所有子节点
  2. 通过自己的节点序号在所有子节点中的顺序
  3. 如果不是最小的子节点,那么进入等待,同时向比自己序号小的最后一个子节点注册 Watcher 监听
  4. 接收到 Watcher 通知后重复 1

10. 参考链接

什么是ZooKeeper?

知乎: zk 的应用场景

从零开始的高并发(四)Zookeeper的分布式队列

Zookeeper-服务注册与发现

基于zookeeper实现统一配置管理


天真真不知路漫漫
70 声望6 粉丝

1