什么是zookeeper及其使用场景

801 阅读6分钟

1. 概述

1.1 分布式应用

分布式应用(distributed application)指的是应用程序分布在不同计算机上,通过网络来共同完成一项任务的工作方式。以javaEE实现一个电商网站为例:

  • 单体应用:所有功能都写在一个项目了;打包成一个可运行的war包;部署这个war包就可以完成整个网站所有功能。
  • 分布式应用:不同的功能写在不同的项目里;打包成多个可运行的war包;由多个运行的服务共同完成整个网站的完整功能。
    在这里插入图片描述

​ 如上图,这个电商网站包含了用户管理、商品管理、订单管理、支付管理4个模块(也称为服务),在分布式应用里面,许多功能都是多个服务共同协作完成的,服务间如何高效有序地协作就成为分布式开发中一个重要的问题。

  • 类比:

    • 单一应用 --> 所有工作都一个人完成
    • 分布式应用 --> 很多工作由多个人共同协作完成

1.2 什么是zookeeper

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它主要是用来解决分布式应用中经常遇到的一些问题。

ZooKeeper从字面意思理解,【Zoo - 动物园,Keeper - 管理员】动物园中有很多种动物,这里的动物就可以比作分布式环境下多种多样的服务,而ZooKeeper做的就是管理这些服务。

1.3 架构

zookeeper的架构如下图所示:
在这里插入图片描述

ZooKeeper集群由一组Server节点组成,这一组Server节点中存在一个角色为Leader的节点,其他节点都为Follower。管与zookeeper架构有如下几个要点:

  • 读操作,直接读取其中一个Follwer并直接返回,

  • 写操作,zookeeper集群会做如下两步处理:

    • 这些请求会被发送到Leader节点上
    • Leader节点上数据变更会同步到集群中其他的Follower节点
  • Leader节点在接收到数据变更请求后,首先将变更写入本地磁盘,以作恢复之用。当所有的写请求持久化到磁盘以后,才会将变更应用到内存中。

  • 注意:持久化到硬盘的数据,只是用于服务重启时数据恢复

  • 当Leader节点出现故障无法正常相应时,集群会自动重新选举一Follwer节点作为Leader。

1.4 存储结构和分层命名空间

ZooKeeper有一个分层的命名空间,结构类似文件系统的目录结构,非常简单而直观。其中,ZNode是最重要的概念。
在这里插入图片描述

在ZooKeeper中每个命名空间(Namespace)被称为ZNode,你可以这样理解,每个ZNode包含一个路径和与之相关的属性和数据,以及继承自该节点的孩子列表。与传统文件系统不同的是,ZooKeeper中的数据保存在内存中,实现了分布式同步服务的高吞吐和低延迟。

ZNode分类及特性:

1.4.1永久节点和临时节点(Ephemeral)

  • 永久节点(默认):一但创建成功就不会自动消失。
  • 临时节点:创建成功后,一但客户端与服务器失去连接,就会自动删除

1.4.2有序节点**(**Sequence Nodes)

​ zookeeper支持创建有序节点,也就是在zNode名称后面自动拼接一个递增的数字。

1.4.3节点的更新与监听(watches)

​ zookeeper客户端可以监听zNode数据的变化,一但zNode的数据发生变化,则自动通知到正在监听的客户端。

02.zookeeper使用场景

2.1 分布式锁

2.1.1 为什么使用分布式锁

一个方法在高并发情况下的同一时间只能被同一个线程执行,在传统单体应用单机部署的情况下,可以使用Java并发处理相关的API(如ReentrantLcok或synchronized)进行互斥控制。但是,随着业务发展的需要,原单体单机部署的系统被演化成分布式系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题.
在这里插入图片描述

2.1.2 基于zookeeper的分布式锁原理

一个分布式锁对应ZooKeeper的一个节点,每个需要获取这个分布式锁的客户端线程在这个节点下创建一个临时有序节点,此时有两种情况:

  1. 创建的临时顺序节点是文件夹下的第一个节点,则认为是获取分布式锁成功。
  2. 创建的临时顺序节点不是文件夹下的第一个节点,则认为当前锁已经被另一个客户端线程获取,此时需要进入阻塞状态,等待节点顺序中的前一个节点释放锁的时候唤醒当前线程
    在这里插入图片描述

2.2 配置中心

对于项目中用到的配置信息(例如数据库地址、用户名、密码……)一般有两种处理方式:

  • 直接放到项目配置文件里,会有如下缺点:

    • 容易导致敏感信息泄露(例如线上的用户名、密码……)
    • 一但配置信息变化(例如数据库密码变化),需要重新打包上线
  • 配置中心,将配置信息维护到配置中心里

    • 流程:

      • 服务启动的时候直接来配置中心获取配置信息
      • 配置信息变化时直接修改配置中心里的数据,配置中心将新的配置信息推送给服务(tomcat),无需重启服务。

2.3 注册中心

在生产环境中需要在一个业务服务器(例如tomcat)中调用另一个业务服务器的接口(例如HTTP接口),那么就需要知道被调用方的IP地址和端口我们有三种方案:

  • IP&端口写死在项目里(不推荐)

    • 优点:简单直接。

    • 缺点:如果被调用方IP地址和端口发生变化或者部署节点的数量发生变化,就需要修改调用方代码重新上线在这里插入图片描述

    • 类比

      • app1–>同学A
      • app2–>同学B
      • IP+端口–>电话号码
      • 调用–>同学A拨打同学B的电话
  • 反向代理服务器,例如nginx

    • 优点:调用方只需要配置Nginx地址,无需关心被调用用者真实的IP地址和端口。
      在这里插入图片描述

    • 类比:

      • nginx --> 班长
      • 反向代理服务器转发–>同学A让班长捎话给同学B
  • 注册中心,例如zookeeper

    • 优点:调用方无需配置被调用方的IP地址,当被调用方的信息变化可以自动更新。

    • 流程:

      • 注册,app2启动时将IP、端口等信息发送到注册中心
      • app1启动时去注册中心拉取app2的信息(IP、端口等信息)
      • 发起调用(例如HTTP请求)
    • 分布式框架dubbo推荐使用zookeeepr作为注册中心。

    • 类比:

      • 注册中心 --> 班主任
      • 注册 -->所有同学在入学的时候都将手机号告诉班主任
      • 拉取目标服务器信息再发起调用–>同学A找班主任要同学B的电话号码再拨打电话

2.4 其他

除了上面列举的三种常见使用场景,zookeeper还可以实现名称服务器、队列、barrier(栅栏)、原子类型、选举等功能,在此不再赘述。