一、分布式系统概念
- 所谓分布式系统,有一个非常容易判断的标准。有人说: 从进程的角度看,两个程序分别运行在两台主机的进程上,它们一起共同完成一个功能。这两个程序组成的系统就叫做分布式系统。 这句话很容易理解,但是这里有几种情况:
至于分布式和微服务的关系,就像是双胞胎兄弟,同一个母亲生的,却有各自的特点。1. 若两个程序实现的功能是一模一样的,此时它们叫做集群。 2. 若两个程序实现的功能不一致,但是为了完成某一个业务,此时它们叫微服务。
- 了解了什么是分布式系统后,若我们的项目由单体架构往分布式方向发展的话,就会带来非常多的问题, 这里只简述如下几个:
具体可参考本人github地址:https://github.com/AvengerEug/distributed1. 系统之间的通信 2. 日志 3. 配置 4. session
二、分布式系统CAP理论
-
CAP原则又称CAP定理,指的是在一个分布式系统中。一致性(Consistency)、可用性(Availability)、分布容错性
(Partition tolerance). 这三个要素最多只能同时实现两个,不可能三者同时实现 -
C(Consistency): 若分布式部署时,有个节点有集群操作。我在集群节点A中对一个内存中的数据做了操作。我想在
节点B中获取到修改后的数据。此时我必须将在节点A修改数据的操作同步到节点B中。而在同步过程中为了保证不出错,
我必须暂停对节点B中针对此数据的请求,待同步完成后再开放请求。 -
A(Availability): 只要用户发送请求就必须响应数据,
-
P(Partition tolerance): 分布式部署时,多台服务器每个模块可能部署在不同机器上,而这些机器可能分布在不
同的区域(比如机器A在中国北京,机器B在湖南长沙),而他们要通信,难免避免不了网络问题而导致的通信失败。 -
C(一致性)和A(可用性)的矛盾: 如要保证一致性,那么必然会存在所有服务暂停使用的情况。这与可用性矛盾了。所以一般的分布式基本上都是实现
CA
或CP
。 具体使用哪种架构,试业务而定。假设与钱有关的,那肯定是要保证一致性, 所以会采取CP
架构。假设在秒杀活动是,为了能保证系统可用,则采取AP
架构
三、分布式系统需要解决的核心问题
- 由上述CAP理论可知,分布式系统需要解决的核心问题就是可用性和一致性。而zookeeper可以利用它的不同角色来扩展节点最终达到可用性或者一致性
四、zookeeper
4.1 从zookeeper集群的角度看待可用性和一致性
- zookeeper集群中的节点有三个角色: leader、follower、observer
- leader是由所有的follower节点选举出来了,当票数过半后才会选举出leader
- 整个集群有了leader才能使用
所以我们接下来将会搭建3个follower和1个observer,具体步骤如下
4.1.1 搭建zookeeper集群
-
下载zookeeper(基于linux)
wget https://downloads.apache.org/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz tar -xf zookeeper-3.4.14.tar.gz -C /root/distributed1/zookeeper
-
这里要说明下集群的注意事项:
- zookeeper集群是否能够启动成功,要看配置文件配置的非observer节点个数以及启动的非observer实例的个数,若过半了,则可以开始进行选举leader节点,选出leader节点后,集群即可使用。我们可以做实验,当配置4个非observer节点时,只有启动了3个实例,zookeeper集群才能被使用(验证是否可以使用, 上述给的搭建集群的步骤中都列出了)
- zookeeper的follower节点增多会影响写操作,因为所有的follower需要进行对写操作进行投票,若节点很多则在投票时会消费大量时间。若想提高读操作的性能,则可以扩容observer的节点。
4.1.2 从不同纬度看待zookeeper的可用性和一致性
- 若将上述的集群搭建成功后,会产生一个leader节点、两个follower节点和一个observer节点。由这样的条件我们可得出如下结论:
非observer节点有3个,也就是说只要有2个非observer节点在运行,那么集群就不会挂
- 下面我们将按照不同实例的运行情况来分析CAP协议:
情况一: 假设我们将leader节点停掉,会发生什么,此时的协议是哪种? => 由上述我们得知的消息可知, 只要有两个非observer节点在运行,集群就不会挂,所以此时存在AP协议,因为集群还是可用的。但是其中会多做一个操作,就是要在剩下的两个非observer节点中重选选出leader 情况二: 假设我们将observer节点停掉,会发生什么?此时的协议是哪种? => 由上述我们得知的消息可知, 只要有两个非observer节点在运行,集群就不会挂. 即使停掉了observer操作,集群的可用性不会受任何影响。此时的协议是AP 情况三: 假设我们将observer节点停掉后重启,会发生什么,此时的协议是哪种? => observer节点重新启动后,会去同步其他节点的数据,此时的协议是CP,保证数据的一致性 情况四: 假设我们将observer节点和一台非observer节点停掉,会发生什么?此时的协议是什么? => 即使停了两台,但是非observer的运行数量2 是大于1.5的,所以依然是可以运行的。此时的协议是AP 情况五:假设我们将两台非observer节点停掉,会发生什么?此时的协议是什么? => 停了两台非observer节点,那么实际上运行的非observer节点数量为1,小于1.5。 此时整个集群将会挂掉,包括observer都会挂掉。
- 通过以上情况可知,zookeeper集群环境中,它的可用性会随着非observer节点存活的数量来决定的。
4.2 zookeeper内部的节点类型
-
持久节点: 即是用create命令创建的节点, 即使服务器重启了,也还存在。命令如下:
create /eug "content"
-
临时节点: 临时节点的生命周期和客户端会话绑定。如果客户端的会话失效,那么这个节点就会自动被清除。(会话失效 != 连接断开)。
在临时节点下不能创建子节点
, 命令如下:create -e /eug "content"
注意事项:
当使用quit
命令退出会话时,再重新连接,可以发现刚刚创建的临时节点不存在了
但是如果按ctrl + c
退出的话,再重新连接,使用ls /
命令查看节点,可以发现临时节点还是存在的。然后我们再按ctrl + c
退出,再重新连接,再次使用ls /
命令查看节点,发现临时节点还是存在,但是过了一段时间后,再使用ls /
命令查看节点,发现它已经不在了。出现这个问题的原因是因为zookeeper
的session
有关。过程是这样的: 在客户端连服务端时,在服务端会生成一个session,然后服务端会做一个倒计时操作,当倒计时结束后,服务端就认为这个session会过期。其中,在服务端做倒计时操作时,如果客户端再次发送了心跳给服务端,那么服务端就会重新倒计时。所以对于客户端而言,如果使用了quit
退出了连接,客户端在关闭连接的时候,会告诉服务端要关闭session,待服务端关闭session后,服务端再删除那个临时节点。如果使用了ctrl + c
退出了连接,但是客户端不会发给服务端任何操作,心跳也不会发送了,若客户端在服务端处理上个session倒计时期间重连了服务端,这个时候还是能看到临时节点,若超过了session的倒计时,那么临时节点将会被删掉。那么问题来了,服务端的倒计时默认到底有多久呢?(这个以后看源码得知)。 当session会话没有过期时,不管我们有多少个客户端连上服务端,都能看到临时节点 -
持久顺序节点: 可以使用如下命令创建顺序节点:
假设已经存在eug节点, 执行完如下命令后,会产生一个叫/eug/seq_000000000
的节点,再执行同样的代码,会产生一个叫/eug/seq_000000001
的节点,所以可知,我们在命令行给节点起的名字只是做前缀使用,并且后面的顺序节点会以递增的方式命名,但要注意,顺序节点的递增是根据父节点而言的,若父节点不同了,那么又会从0开始递增create -s /eug/seq_ 1
4.3 zookeeper watch机制
- zookeeper服务端在进行如下操作时,会对当前操作的节点发布一个事件,具体的事件类型如下:
None (-1), // 连接时 NodeCreated (1), // 节点被创建 NodeDeleted (2), // 节点被删除 NodeDataChanged (3), // 节点内容被修改 NodeChildrenChanged (4); // 子节点内容被修改
- zookeeper有三个客户端,分别是:
zookeeper原生客户端、ZkClient、curator
zookeeper原生客户端
有一个特性: 监听器被执行完一次后,不会再被执行
4.4 zookeeper acl操作
-
zookeeper会在每个节点上添加一些权限信息, 并具有如下特性:
1. zookeeper的权限控制是基于节点来的,若对父节点有cdrw权限,但这不意味着对子节点也有同样的权限。所以还需要对子节点重新赋值权限才行 2. 子节点不会继承父节点的权限,他们都是独立的。所以客户端有可能无法访问父节点,但他有可能能访问子节点 3. 每个节点有多个权限控制方案和多个权限
-
格式: schema:ID:permission
schema表示权限的策略、 ID表示授权对象 permission表示具体的权限 schema可取: 1. world: 只有一个用户, 与id为anyone是配套的,如果写了world那么后面就是anyone, 比如: setAcl /eug world:anyone:c 表示/eug这个节点对于任何人而言是可以创建子节点的 2. anyone: 所有人 3. ip: 指定ip 4. auth: 已添加认证的用户认证 5. digest: 使用"用户名:密码"方式认证 ============= id可取: 1. world: 只有一个id时,表示anyone 2. ip: 通常是一个ip地址或者地址段,比如192.168.0.110或者192.168.0.1/24 3. auth: 用户名 4. digest: 自定义通常是"username:BASE64(SHA-1(username:password))" ============ 权限可取: 1. CREATE, 简写c,表示创建子节点 2. DELETE, 简写d,表示删除子节点 3. READ, 简写r,可以读取节点信息及显示子节点列表 4. WRITE, 简写w, 看设置节点数据 5. ADMIN, 简写a, 可以设置节点控制列表
-
案例
-
为/eug节点添加任何人都可以写的操作
# 后面加了一个a是方便后续重新为/eug节点赋值权限 setAcl /eug world:anyone:wa
-
为/eug节点添加指定ip才能操作
setAcl /eug ip:192.168.0.113:wa
-
为/eug节点添加只有指定用户才能进行写操作
# 先新增一个用户 addauth digest zhangsan:123 # 为节点添加指定权限 setAcl /eug auth:zhangsan:123:ra # 当其他客户端要读取/eug节点的话要怎么办呢? # 首先先做一个类似认证的操作, 如下 addauth digest zhangsan:123 # 再查看节点信息 get /eug
-
-
获取权限信息
[zk: localhost:2181(CONNECTED) 22] getAcl /eug 'world,'anyone : cdrwa -> cdrw 表示create、delete、read、write、admin