PS : 文中的所有案例,均来自下面参考文章中的例子。
我只是在上面加了一点点结合公司业务场景的理解
言归正传,直接进入整体。
1、统一的配置管理
前记:个人理解,原则上说,只要可以实现 k-v 存储的,都可以用来做统一的配置中心。
你甚至可以使用redis 作为配置中心的管理。
对比之下,我还是推荐使用针对配置更为专业的 Apollo 来管理。
先不跑偏,回到我们的主题,我们结合 Zookeeper 来实现一个配置中心。
1.1 配置特性
在此之前,我们先认为下配置管理中心一般会有哪些特性:
- 数据量很小的 k-v
- 配置信息会在程序运行期间发生动态变化
- 集群之间共享,配置一致
而 Zookeeper 的 Znode 中的 临时节点刚好可以满足上面的条件要求。
1.2 Zookeeper 的方案特性
方案设计:
说明:
ZooKeeper 采用的是推拉结合的方式。
-
推
: 服务端会推给注册了监控节点的客户端 Wathcer 事件通知 -
拉
: 客户端获得通知后,然后主动到服务端拉取最新的数据
1.3 案例
比如我们现在有三个系统A、B、C,他们有三份配置,分别是ASystem.yml、BSystem.yml、CSystem.yml
,然后,这三份配置又非常类似,很多的配置项几乎都一样。
- 此时,如果我们要改变其中一份配置项的信息,很可能其他两份都要改。并且,改变了配置项的信息很可能就要重启系统
于是,我们希望把ASystem.yml、BSystem.yml、CSystem.yml相同的配置项抽取出来成一份公用的配置common.yml,并且即便common.yml改了,也不需要系统A、B、C重启。
做法:我们可以将common.yml
这份配置放在ZooKeeper的Znode节点中,系统A、B、C监听着这个Znode节点有无变更,如果变更了,及时响应。
推广 :
日常的开发中,比如我们的 PHP 对应的 config 文件.
同样的还有 DB 文件,在开发、测试、预发布、生产各自的配置文件不同。可以通过配置中心统一管理。
参考 Apollo
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对应的服务器,都有可能发生故障等失去连接。
PS : 统一的场景,类似这种负载均衡的分配的。我们公司目前使用的结合 consul + HA + Nginx
2.2 案例
服务的提供方,注册到Consul 。
当用户通过浏览器访问 https://ld.xxx.com/v2/oss_corp 时
nginx 会分解upstream 选择对应的一个服务
可以参考内网访问地址 : http://192.168.1.193/upstream_list
根据算法,会选择consul 的注册上的服务一个,转发到对应的服务器上。
同时,当那个服务器节点,断线之后,consul 会自动发现更新配置。新来的请求,不会转发到对应的服务节点上。
负载均衡是一种手段,用来把对某种资源的访问分摊给不同的设备,从而减轻单点的压力。
实现的思路:
- 首先建立 Servers 节点,并建立监听器监视 Servers 子节点的状态(用于在服务器增添时及时同步当前集群中服务器列表)
- 在每个服务器启动时,在 Servers 节点下建立临时子节点 Worker Server,并在对应的字节点下存入服务器的相关信息,包括服务的地址,IP,端口等等
- 可以自定义一个负载均衡算法,在每个请求过来时从 ZooKeeper 服务器中获取当前集群服务器列表,根据算法选出其中一个服务器来处理请求
3、服务注册和服务发现
使用场景不同,但是原理大同小异。下面的我就简单论述了。(和上面的例子差不多)
对应的模型:
步骤:
- 将产品服务的信息注册到zookeeper的节点上
- 然后获取到节点上的信息并存储起来(本文存到List)
- Watcher机制监控List里数据的变化并更新数据 (假如产品服务2挂了通过监听机制将其移出)
- 利用轮询或者hash等算法去获取List里的数据供订单服务调用(负载均衡)
映射图如下:
4、分布式锁
系统A、B、C 都去访问/locks
节点
访问的时候会创建带顺序号的临时/短暂(EPHEMERAL_SEQUENTIAL
)节点,比如,系统A创建了id_000000
节点,系统B创建了id_000002
节点,系统C创建了id_000001
节点。
接着,拿到/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
创建完节点后,根据以下步骤确定执行顺序:
- 通过
get_children()
接口获取/queue_fifo
节点下所有子节点 - 通过自己的节点序号在所有子节点中的顺序
- 如果不是最小的子节点,那么进入等待,同时向比自己序号小的最后一个子节点注册 Watcher 监听
- 接收到 Watcher 通知后重复 1
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。