0. 前言

为测试方便,电脑最好可以跑多台虚拟机,或者有2-3个云服务器(学生机9.9一月,可以买一个轻量级和ECS,或者借同学的用一下)

1. Dubbo 与 SpringCloud对比


  • dubbozookeeper + dubbo + springmvc + springboot

    • 官方地址:http://dubbo.apache.org/#!/?lang=zh-cn
    • 配套通信方式:rpc 性能高
    • 注册中心:zookeper/redis
    • 配置中心:diamond(企业很少用)

  • springcloud: 全家桶 + 轻松嵌入第三方组件(Netflix 奈飞)

    • 官网:http://projects.spring.io/spring-cloud/
    • 配套通信方式:http restful
    • 注册中心:eruka/consul
    • 配置中心:config
    • 断路器:hystrix
    • 网关:zuul
    • 分布式追踪系统:sleuth+zipkin

  • 二者对比:

Dubbo

SpringCloud

服务注册中心

Zookeeper

Spring Cloud Netfilx Eureka

服务调用方式

RPC

REST API/HTTP

服务监控

Dubbo-monitor

Spring Boot Admin

断路器

不完善

Spring Cloud Netfilx Hystrix

服务网关

Spring Cloud Netfilx Zuul

分布式配置

Spring Cloud Config

服务跟踪

Spring Cloud Sleuth

消息总栈

Spring Cloud Bus

数据流

Spring Cloud Stream

批量任务

Spring Cloud Task

最大区别:Spring Cloud 抛弃了Dubbo的RPC通信,采用的是基于HTTP的REST方式

严格来说,这两种方式各有优劣。虽然从一定程度上来说,后者牺牲了服务调用的性能,但也避免了上面提到的原生RPC带来的问题。而且REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖,这个优点在当下强调快速演化的微服务环境下,显得更加合适。

2. 微服务注册中心


简介:什么是注册中心,常用的注册中心有哪些?



  • 注册中心:服务管理,核心是有个服务注册表,心跳机制动态维护;
  • 服务提供者provider: 启动的时候向注册中心上报自己的网络信息
  • 服务消费者consumer: 启动的时候向注册中心上报自己的网络信息,拉取provider的相关网络信息


为什么要用注册中心?


微服务应用和机器越来越多,调用方需要知道接口的网络地址,如果靠配置文件的方式去控制网络地址,对于动态新增机器,维护带来很大问题!


主流的微服务注册中心



  • zookeeper(与Dubbo结合使用)
  • Eureka(与SpringCloud结合使用)
  • consul(与SpringCloud结合使用)
  • etcd(与SpringCloud结合使用)

多台阿里云服务器上搭建ZooKeeper集群_分布式

3. Zookeeper下载与安装

[Zookeeper官网下载地址][https://zookeeper.apache.org/releases.html#download]

3.1 在CentOS下安装Zookeeper

注意: Zookeeper 的安装依赖于jdk环境!所以先确保服务器活着虚拟机中配置过了jdk环境!


  • 将下载的zookeeper压缩包上传到云服务器,我自己是将所有压缩包放到**/usr/local/src/software/ **下

    • ​tar -zxvf zookeeper-3.4.14.tar.gz​​ 解压zookeeper压缩包
    • ​mv zookeeper-3.4.14 zookeeper​​ 修改文件夹名字为zookeeper方便记忆
    • 在zookeeper目录下创建data 目录​​mkdir data​​,并​​pwd​​获取data目录的路径**/usr/local/src/software/zookeeper/data**
    • ​cd /usr/local/src/software/zookeeper/conf​​ 进入conf目录下

      • ​mv zoo_sample.cfg zoo.cfg​​修改zoo_sample.cfg文件名称为zoo.cfg
      • ​vim zoo.cfg​​ 修改配置文件,wq保存退出:
        多台阿里云服务器上搭建ZooKeeper集群_spring_02

    • ​useradd zookeeper​​ 创建一个名为zookeeper的用户
    • ​chown -R zookeeper:zookeeper zookeeper/​​ 将zookeeper文件夹权限交给刚添加的zookeeper用户
      多台阿里云服务器上搭建ZooKeeper集群_分布式_03

  • 然后我们就可以进行启动zookeeper操作了

    • 我们进入zookeeper的bin目录中:
      多台阿里云服务器上搭建ZooKeeper集群_分布式_04
    • ​./zkServer.sh start​​ 启动zookeeper:
      多台阿里云服务器上搭建ZooKeeper集群_linux_05

  • 为了验证是否启动成功,我们来使用客户端连接一下zookeeper服务:

    • 先使用​​netstat -tlun​​ 查看端口服务开启情况:
      多台阿里云服务器上搭建ZooKeeper集群_分布式_06
    • 建议可以直接把防火墙关闭,这样就不用在防火墙中添加规则了
    • 阿里云安全组开端口
      多台阿里云服务器上搭建ZooKeeper集群_子节点_07
    • 然后我们在本机电脑(windows下zookeeper下载好解压即可使用)上用zookeeper的客户端连接下服务器的zookeeper

      • ​.\zkCli.cmd -timeout 5000 -server X.XXX.XX.XXX:2181​​ ip地址为自己服务其的ip
        多台阿里云服务器上搭建ZooKeeper集群_zookeeper_08
        多台阿里云服务器上搭建ZooKeeper集群_linux_09
      • 完成连接zookeeper!



3.2 ZooKeeper数据模型

多台阿里云服务器上搭建ZooKeeper集群_linux_10


  • 每一个节点都叫做zNode,可以有子节点,也可以有数据
  • 每个节点都能设置相应的权限控制用户的访问
  • 每个节点存储的数据不宜过大
  • 每个节点都带有一个版本号,数据变更时,版本号变更(乐观锁)
  • 节点分永久节点跟临时节点

4. Zookeeper 常用命令之zkCli


  • ​chown -R zookeeper:zookeeper zookeeper/​​ 将zookeeper文件夹权限赋给zookeeper用户
  • ​su zookeeper​​ 切换到zookeeper用户
  • 然后进入zookeeper的bin目录下执行​​./zkServer.sh start​​启动zookeeper,关闭zookeeper ​​./zkServer.sh stop​
    • 如果出现下面错误信息,说明zookeeper权限赋于失败,重新​​su root​​ 切换root 用户再次将整个zookeeper文件夹的权限赋给zookeeper用户:
      多台阿里云服务器上搭建ZooKeeper集群_zookeeper_11
  • 然后即可成功启动:
    多台阿里云服务器上搭建ZooKeeper集群_spring_12
  • 然后我们就可以来学习下zkCli 命令

4.1 zkCli命令


  • 启动zkCli ,在bin目录下执行​​./zkCli.sh -h​
  • 关闭zkCli,​​close​

ZooKeeper -server host:port cmd args
stat path [watch]
set path data [version]
ls path [watch]
delquota [-n|-b] path
ls2 path [watch]
setAcl path acl
setquota -n|-b val path
history
redo cmdno
printwatches on|off
delete path [version]
sync path
listquota path
rmr path
get path [watch]
create [-s] [-e] path data acl
addauth scheme auth
quit
getAcl path
close
connect host:port

创建节点和查看节点


  • ​create [-s] [-e] path acl​​ 创建节点

    • -s : 表示创建顺序节点
    • -e : 表示创建临时节点
    • data : 表示创建的节点的数据内容
      多台阿里云服务器上搭建ZooKeeper集群_子节点_13

  • 查看节点

    • 获取根节点:​​ls /​
    • 获取某个节点的子节点:​​ls /path​​,eg:​​ls /csp0020000000001​
    • 获取某个节点的数据:​​get /path​​​[zk: localhost:2181(CONNECTED) 5] get /csp0010000000000csp001 --- > 节点的值cZxid = 0x5 ---> 事务idctime = Wed Nov 18 16:06:18 CST 2020 ---> 节点创建的时间mZxid = 0x5 ---> 最后一次更新时的事务idmtime = Wed Nov 18 16:06:18 CST 2020 ---> 最后一次更新的时间pZxid = 0x5 ---> 该节点的子节点列表最后一次被修改的事务idcversion = 0 ---> 子节点列表的版本dataVersion = 0 ---> 数据内容的版本aclVersion = 0 ---> acl版本ephemeralOwner = 0x0 ---> 用于临时节点,表示创建该临时节点的事务id 如果当前节点不是临时节点,则该字段值为0 dataLength = 6 ---> 数据内容的长度numChildren = 0 ---> 子节点的数量 ​
    • 查看某个节点的状态:​​stat /path​​​[zk: localhost:2181(CONNECTED) 4] stat /csp0020000000001 cZxid = 0x5 ---> 事务idctime = Wed Nov 18 16:06:18 CST 2020 ---> 节点创建的时间mZxid = 0x5 ---> 最后一次更新时的事务idmtime = Wed Nov 18 16:06:18 CST 2020 ---> 最后一次更新的时间pZxid = 0x5 ---> 该节点的子节点列表最后一次被修改的事务idcversion = 0 ---> 子节点列表的版本dataVersion = 0 ---> 数据内容的版本aclVersion = 0 ---> acl版本ephemeralOwner = 0x0 ---> 用于临时节点,表示创建该临时节点的事务id 如果当前节点不是临时节点,则该字段值为0 dataLength = 6 ---> 数据内容的长度numChildren = 0 ---> 子节点的数量 ​
    • 获取某个节点的子节点以及当前节点的状态:​​ls2 /path​​​[zk: localhost:2181(CONNECTED) 6] create -e /csp0010000000000/csp1999 my name is csp #为该节点添加临时子节点 Created /csp0010000000000/csp1999 [zk: localhost:2181(CONNECTED) 7] ls2 /csp0010000000000 #获取该节点的子节点以及当前节点的状态[csp1999] #该节点的子节点 cZxid = 0x4ctime = Wed Nov 18 16:05:27 CST 2020mZxid = 0x4mtime = Wed Nov 18 16:05:27 CST 2020pZxid = 0xccversion = 1dataVersion = 0aclVersion = 0ephemeralOwner = 0x0dataLength = 6numChildren = 1 [zk: localhost:2181(CONNECTED) 8]


修改节点

  • 修改节点的数据:​​set /path data [version]​​​[zk: localhost:2181(CONNECTED) 10] set /csp0010000000000 HelloWorld cZxid = 0x4ctime = Wed Nov 18 16:05:27 CST 2020mZxid = 0xdmtime = Wed Nov 18 16:28:10 CST 2020pZxid = 0xccversion = 1dataVersion = 1 # dataVersion 由0变为1 aclVersion = 0ephemeralOwner = 0x0dataLength = 10numChildren = 1 [zk: localhost:2181(CONNECTED) 11] get /csp0010000000000HelloWorld cZxid = 0x4ctime = Wed Nov 18 16:05:27 CST 2020mZxid = 0xdmtime = Wed Nov 18 16:28:10 CST 2020pZxid = 0xccversion = 1dataVersion = 1 # dataVersion 由0变为1 aclVersion = 0ephemeralOwner = 0x0dataLength = 10numChildren = 1 # dataVersion 其实就相当于MySQL中的乐观锁,即数据的版本号# 如果set 命令修改节点的数据时,附带上version,如果版本号不匹配,就不能修改!# 例如:[zk: localhost:2181(CONNECTED) 12] set /csp0010000000000 HelloWorld 0version No is not valid : /csp0010000000000 # version 不匹配,不能修改[zk: localhost:2181(CONNECTED) 13] set /csp0010000000000 HelloWorld 2 version No is not valid : /csp0010000000000 # version 不匹配,不能修改[zk: localhost:2181(CONNECTED) 14] set /csp0010000000000 HelloWorld 1 # version匹配修改成功! cZxid = 0x4ctime = Wed Nov 18 16:05:27 CST 2020mZxid = 0x10mtime = Wed Nov 18 16:33:34 CST 2020pZxid = 0xccversion = 1dataVersion = 2 # dataVersion 由1变为2 aclVersion = 0ephemeralOwner = 0x0dataLength = 10numChildren = 1 ​

删除节点


  • ​delete /path [version]​​ 非递归删除
  • ​rmr /path [version]​​ 递归删除

[zk: localhost:2181(CONNECTED) 15] ls /
[csp0010000000000, csp0040000000003, zookeeper, csp0020000000001, csp0030000000002]
[zk: localhost:2181(CONNECTED) 16] delete /csp0030000000002 # 该节点没有子节点,可以直接删除
[zk: localhost:2181(CONNECTED) 17] ls /
[csp0010000000000, csp0040000000003, zookeeper, csp0020000000001] # 删除成功!
[zk: localhost:2181(CONNECTED) 18] delete /csp0010000000000 # 该节点有子节点,不可以直接删除
Node not empty: /csp0010000000000 # 该节点有子节点,删除失败!
[zk: localhost:2181(CONNECTED) 19] rmr /csp0010000000000 # 使用递归删除
[zk: localhost:2181(CONNECTED) 20] ls /
[csp0040000000003, zookeeper, csp0020000000001] # 删除成功!

5. Zookeeper 中的Session 机制 与 Watcher 机制

5.1 Zookeeper 中的Session 机制


  • 用于客户端与服务端之间的连接,可设置超时时间,通过心跳包的机制(客户端向服务端ping包请求)检查心跳结束,session就过期
  • session过期的时候,该session创建的所有临时节点都会被抛弃

5.2 Zookeeper 中的Watcher 机制

多台阿里云服务器上搭建ZooKeeper集群_zookeeper_14


  • 对节点的watcher操作 get/stat + watch针对每一个节点的操作,都可以有一个监控者,当节点发生变化,会触发watcher事件zk中watcher是一次性的,触发后立即销毁所有有监控者的节点的变更操作都能触发watcher事件
    • 为什么说watch事件是一次性的呢?看下实操:
      多台阿里云服务器上搭建ZooKeeper集群_子节点_15
      多台阿里云服务器上搭建ZooKeeper集群_spring_16
      前面哪个客户端打印的结果是:
      多台阿里云服务器上搭建ZooKeeper集群_linux_17
  • 子节点的watcher操作(监控父节点,当父节点对应的子节点发生变更的时候,父节点上的watcher事件会被触发)​​ls/ls2 + watch​​ 增删会触发、修改不会,如果子节点再去新增子节点,不会触发(也就是说,触发watcher事件一定是直系子节点

6. Zookeeper 中的 ACL 权限控制


  • 针对节点可以设置相关的读写等权限,目的是为了保证数据的安全性
  • 权限permissions可以指定不同的权限范围及角色
  • 常用的命令

    • getAcl 获取节点权限
    • setAcl 设置节点权限
    • addauth 输入认证授权信息,注册时输入明文密码,但是在zk里,以加密的形式保存

  • [zk: localhost:2181(CONNECTED) 3] rmr /csp0001 [zk: localhost:2181(CONNECTED) 4] create /csp0001 HelloWorld Created /csp0001 [zk: localhost:2181(CONNECTED) 5] getAcl /csp0001 # 获取节点权限'world,'anyone : cdrwa # acl 的组成[scheme:id: permissions]
  • acl 的组成**[scheme: id:permissions]** ,即:【约束:id:权限】
  • scheme 授权机制

    • world 下只有一个id,也就是​​anyone​​​,表示所有人 ​​world:anyone:permissions​
    • auth 代表认证登录,需要注册用户有权限才可以 ​​auth:user:password:permissions​
    • digest 需要密码加密才能访问 ​​digest:username:BASE64(SHA1(password)):permissions​​(跟auth区别在于,auth明文,digest为密文)
    • ip 例如:​​ip:localhost:psermissions​
    • super 代表超管,拥有所有的权限;
    • 打开zk目录下的/bin/zkServer.sh服务器脚本文件,找到如下一行:

  • id:允许访问的用户
  • permissions:权限组合字符串
    ​cdrwa:c --- > create 创建子节点d --- > delete 删除子节点r --- > read 读取节点的数据w --- > write 写入数据a --- > admin 可管理的权限 cdrwa cdr cdw ​
  • acl的使用场景:
  • 开发环境跟测试环境,使用acl就可以进行分离,开发者无权去操作测试的节点,生产环境上控制指定ip的服务可以访问相关的节点

7. Zookeeper 中的三种角色及其选举机制

7.1 三种角色及其作用


  • leader:作为整个zk集群写请求的唯一处理者,并负责进行投票的发起和决议,更新系统的状态
  • follower接收客户端请求,处理读请求,并向客户端返回结果;将写请求转给 Leader;在选举 Leader过程中参与投票。
  • observer:可以理解为无选举投票权的 Flollower,其主要是为了协助 Follower 处理更多的读请求。如果 Zookeeper 集群的读请求负载很高,或者客户端非常非常多,多到跨机房,则可以设置一些 Observer 服务器,以提高读取的吞吐量。

7.2 注册中心常见的三种模式


  • zk的核心是广播机制,该机制保证了各个zk之间数据同步(数据一致性)zk实现的机制为ZAB协议
  • 恢复模式: 如果leader崩溃,这个时候就会进入恢复模式,使整个zk集群恢复到正常的工作状态
  • 同步模式:新的leader选举出来后,就乎进入同步模式(各个follower会去同步新的leader上的数据),当大多数zkServer完成了与leader的状态同步之后,恢复模式就结束
  • 广播模式:客户端想写入数据,这个时候leader发起提议,当leader的提议被大多数的zkServer统一之后,leader就会去修改自身的数据,并将修改后的数据广播给其他的follower

7.3 Zookeeper 的选举机制


  • myid
    这是 zk 集群中服务器的唯一标识,称为 myid。例如,有三个 zk 服务器,那么编号分别是 1,2,3。
  • zxid
    ​ReentranReadWriteLock 32位 高位 低位 0000000000000000 0000000000000000 epoch xid 00000000000000000000000000000000 00000000000000000000000000000000 zxid 为 Long 类型,其中高 32 位表示 epoch,低 32 位表示 xid。即 zxid 由两部分构成:epoch 与 xid。 每个 Leader 都会具有一个不同的 epoch 值,表示一个时期、时代。新的 Leader 产生,则会更新所有zkServer 的 zxid 中的 epoch。 而 xid 则为 zk 的事务 id,每一个写操作都是一个事务,都会有一个 xid。每一个写操作都需要由 Leader 发起一个提议,由所有 Follower 表决是否同意本次写操作。 ​
  • 逻辑时钟
    逻辑时钟,Logicalclock,是一个整型数,该概念在选举时称为 logicalclock,而在 zxid 中则为 epoch 的值。即 epoch 与 logicalclock 是同一个值,在不同情况下的不同名称。
  • zk的选举状态
    ​LOOKING,选举状态(查找 Leader 的状态)。 LEADING,领导者状态。处于该状态的服务器称为 Leader。 FOLLOWING,随从状态,同步 leader 状态。处于该状态的服务器称为 Follower。 OBSERVING,观察状态,同步 leader 状态。处于该状态的服务器称为 Observer。 ​

8. Zookeeper 集群搭建实操

首先我准备了三台阿里云服务器(如果没有服务器,可以考虑克隆自己本地的虚拟机克隆3台):

多台阿里云服务器上搭建ZooKeeper集群_分布式_18

然后按照章节3 的步骤,为其他两个服务器搭建好zookeeper环境!

注意:搭建zookeeper集群的时候别忘了给阿里云服务器安全组的对应端口都开放!


  • 端口的作用

    • 2181 对client端提供服务
    • 2888 集群内及其通讯使用的端口
    • 3888 集群选举leader

  • 修改三台服务器的 zk 配置

    • 进入zookeeper的conf目录下修改zoo.cfg,添加如下内容:
      ​dataDir=/usr/local/src/software/zookeeper/data # the port at which the clients will connect clientPort=2181server.1 =8.XXX.XX.136:2182:2183server.2 =8.XXX.XX.45:2182:2183server.3 =39.XXX.XXX.61:2182:2183 ​
    • 在zk的根目录下,新建一个data目录,并在data目录下新增一个myid的文件
    • 将修改好配置的zk,分别放到三台服务器的**/usr/local/src/software/zookeeper**中,并将目录权限改为zookeeper用户
    • 三台服务器,分别新增一个叫做zookeeper的用户 ​​useradd zookeeper​
    • 三台服务器,均修改 **/usr/local/src/software/zookeeper/data/**目录里的myid文件,文件内容是一个数字,对应server.1=8.XXX.XX.136:2182:2183 里的1,依次类推!
    • 三台服务器的zk的权限,都赋给zookeeper用户​​chown -R zookeeper:zookeeper zookeeper/​
    • 关闭防火墙 : ​​systemctl stop firewalld.service​
    • 进入zk的bin目录:​​rm -rf *.cmd​​,然后​​chmod +x *.sh​
    • 从第一台机子开始依次启动zookeeper服务:​​./zkServer.sh start​
    • 三台机子均启动完成之后,可以使用​​zkServer.sh status​​去查看状态