PaaS平台所使用的服务发现配置存储中心——etcd

概述

​ etcd是什么?它是一个键值存储仓库,用于配置共享和服务发现(A highly-available key value store for shared configuration and service discovery)。

​ etcd具有以下特点:

  • 简单:基于HTTP+JSON的API,用curl命令就可以轻松使用。
  • 安全:可选SSL客户认证机制。
  • 快速:每个实例每秒支持一千次写操作。
  • 可信:使用Raft算法充分实现了分布式。

​ 由此可见,etcd主要解决的是分布式系统中数据一致性的问题,而分布式系统中的数据分为控制数据和应用数据。etcd处理的数据默认为控制数据,对于应用数据,它只推荐处理数据量很小但更新访问频繁的情况。

etcd常见应用场景

场景一:服务发现

​ 服务发现要解决的问题是什么呢?在同一个分布式集群中的进程或服务,互相感知并建立连接,这就是服务发现。从本质上说,服务发现就是想要了解集群中是否有进程在监听UDP或TCP端口,并且通过对应的字符串(名字)信息就可以进行查找和连接。要解决服务发现的问题,需要有以下三大支柱,缺一不可:

  • 一个强一致性、高可用的服务存储目录。基于Raft算法的etcd天生就是这样一个强一致性、高可用的服务存储目录。
  • 一种注册服务和监控服务健康状态的机制。用户可以在etcd中注册服务,并且对注册的服务设置key TTL,定时保持服务的心跳以达到监控健康状态的效果。
  • 一种查找和连接服务的机制。通过在etcd指定的主题下注册的服务也能在对应的主题下被查找到。为了确保连接,可以在每个服务机器上都部署一个Proxy模式的etcd,这样就可以确保能访问etcd集群的服务都能互相连接。

服务发现的具体应用场景:

  • 微服务协同工作架构中,服务动态添加。随着Docker容器的流行,多种微服务共同协作,构成一个功能相对强大的架构的案例越来越多。透明化地动态添加这些服务的需求也日益强烈。通过服务发现机制,在etcd中注册某个服务名字的目录,在该目录下存储可用的服务节点的IP。在使用服务的过程中,只要从服务目录下查找可用的服务节点使用即可。如图所示,以Docker为承载的前端在服务发现目录中查找到可用的中间件,中间件再找到服务后端,以此快速构建起一个动态和高可用的架构。

    在这里插入图片描述

  • PaaS平台中应用多实例与实例故障重启透明化。PaaS平台中的应用一般都有多个实例,通过域名,不仅可以透明地对多个实例进行访问,而且还可以实现负载均衡。但是应用的某个实例随时都有可能故障重启,这时就需要动态地配置域名解析(路由)中的信息。通过etcd的服务发现功能就可以轻松解决这个动态配置的问题,如图所示。

    在这里插入图片描述

场景二:消息发布与订阅

扫描二维码关注公众号,回复: 5671910 查看本文章

​ 在分布式系统中,最合适的组件间通信方式是消息发布与订阅机制。具体而言,即构建一个配置共享中心,数据提供者在这个配置中心发布消息,而消息使用者则订阅他们关心的主题,一旦相关主题有消息发布,就会实时通知订阅者。通过这种方式可以实现分布式系统配置的集中式管理与实时动态更新。

场景三:负载均衡

​ 在分布式系统中,为了保证服务的高可用以及数据的一致性,通常都会部署多份数据和服务,以此达到对等服务,即使其中某一个服务失效了,也不影响使用。这样的实现虽会在一定程度上导致数据写人性能的下降,但却能实现数据访问时的负载均衡。因为每个对等服务节点上都存有完整的数据,所以用户的访问流量就可以分流到不同的机器上。

场景四:分布式通知与协调

​ 这里讨论的分布式通知与协调,与消息发布与订阅有些相似。两者都使用了etcd中的Watcher机制,通过注册与异步通知机制,实现分布式环境下不同系统之间的通知与协调,从而对数据变更进行实时处理。具体的实现方式通常为:不同系统都在etcd上对同一个目录进行注册,同时设置Watcher监控该目录的变化,当某个系统更新了etcd的目录,那么设置了Watcher的系统就会收到通知,并作出相应处理。

  • 通过etcd进行低耦合的心跳检测。检测系统和被检测系统通过etcd上某个目录关联而非直接关联起来,这样可以大大减少系统的藕合性。
  • 通过etcd完成系统调度。某系统由控制台和推送系统两部分组成,控制台的职责是控制推送系统进行相应的推送工作。管理人员在控制台做的一些操作,实际上只需要修改etcd上某些目录节点的状态,etcd就会自动把这些变化通知给注册了Watcher的推送系统客户端,推送系统再作出相应的推送任务。
  • 通过etcd完成工作汇报。大部分类似的任务分发系统会在子任务启动后,到etcd来注册一个临时工作目录,并且定时将自己的进度进行汇报(即将进度写入到这个临时目录),这样任务管理者就能够实时知道任务进度。

场景五:分布式锁与竞选

​ etcd使用Raft算法保持了数据的强一致性,某次操作存储到集群中的值必然是全局一致的,因此很容易实现分布式锁。锁服务有两种使用方式,一是保持独占,二是控制时序。

  • 保持独占。即所有试图获取锁的用户最终只有一个可以得到。etcd为此提供了一套实现分布式锁原子操作CAS ( CompareAndSwap)的API。通过设置prevExist值,可以保证在多个节点同时创建某个目录时,只有一个成功,而该用户即可认为是获得了锁。
  • 控制时序。即所有试图获取锁的用户都会进入等待队列,获得锁的顺序是全局唯一的,同时决定了队列的执行顺序。etcd为此也提供了一套API(自动创建有序键),对一个目录键值时指定为POST动作,这样etcd会自动在目录下生成一个当前最大的值为键,存储这个新的值(客户端编号),同时还可以使用API按顺序列出所有当前目录下的键值。此时这些键的值就是客户端的时序,而这些键中存储的值可以是代表客户端的编号。

​ 另外,使用分布式锁可以完成Leader竞选。对于一些长时间的CPU计算或者使用I/O操作,只需要由竞选出的Leader计算或处理一次,再把结果复制给其他Follower即可,从而避免重复劳动,节省计算资源。

场景六:分布式队列

​ 分布式队列的常规用法与场景五中所描述的分布式锁的控制时序用法类似,即创建一个先进先出的队列,保证顺序。

场景七:集群监控

​ 通过etcd来进行监控,实现起来非常简单并且实时性强,主要用到了以下两点特性:

  • 前面几个场景已经提到了Watcher机制,当某个节点消失或有变动时,Watcher会第一时间发现并告知用户。
  • 节点可以设置TTL key,例如,每隔30秒向etcd发送一次心跳,代表该节点仍然存活;否则说明节点消失。

etcd实现原理

etcd架构与术语表

​ etcd的架构并不复杂,如图所示,etcd主要分为4个部分:

  • HTTP Server:用于处理用户发送的API请求以及其他etcd节点的同步与心跳信息请求。
  • Store:用于处理etcd支持的各类功能的事务,包括数据索引、节点状态变更、监控与反馈、事件处理与执行等。它是etcd对用户提供的大多数API功能的具体实现。
  • Raft : Raft强一致性算法的具体实现,是etcd的核心。
  • WAL:即Write Ahead Log(预写式日志),它是etcd的数据存储方式。除了在内存中存有所有数据的状态以及节点的索引以外,etcd还通过WAL进行持久化存储。WAL中,所有的数据在提交前都会事先记录日志。Snapshot是为了防止数据过多而进行的状态快照;Entry则表示存储的具体日志内容。

在这里插入图片描述

​ 通常一个用户的请求发送过来,会经由HTTP Server转发给Store进行具体的事务处理,如果涉及节点的修改,则交给Raft模块进行状态的变更、日志的记录,然后再同步给别的etcd节点以确认数据提交,最后进行数据的提交,再次同步。

etcd中涉及较多术语,为便于理解,现罗列如下:

  • Raft : etcd所采用的保证分布式系统强一致性的算法。
  • Node:一个Raft状态机实例。
  • Member:一个etcd实例,管理着一个Node,可以为客户端请求提供服务。
  • Cluster:由多个Membe的成的可以协同工作的etcd集群。
  • Peer:对同一个etcd集群中另外一个Member的称呼。
  • Client:向etcd集群发送HTTP请求的客户端。
  • WAL:预写式日志,是etcd用于持久化存储的日志格式。
  • Snapshot: etcd防止WAL文件过多而设置的快照,存储etcd数据状态。
  • Proxy: etcd的一种模式,为etcd集群提供反向代理服务。
  • Leader: Raft算法中通过竞选而产生的处理所有数据提交的节点。
  • Follower:竞选失败的节点作为Raft中的从属节点,为算法提供强一致性保证。
  • Candidate: Follower超过一定时间接收不到Leader的心跳时,转变为Candidate开始Leader竞选。
  • Term:某个节点成为Leader到下一次竞选开始的时间周期,称为一个Term。
  • Index:数据项编号。Raft中通过Term和Index来定位数据。

etcd数据存储原理

​ etcd的存储分为内存存储和持久化(硬盘)存储两部分。内存中的存储除了顺序化地记录所有用户对节点数据变更的记录外,还会对用户数据进行索引、建堆等方便查询的操作。而持久化则使用WAL进行记录存储。

​ 在WAL的体系中,所有的数据在提交之前都会进行日志记录。在etcd的持久化存储目录中有两个子目录。一个是WAL,存储着所有事务的变化记录;另一个则是Snapshot,用于存储某一个时刻etcd所有目录的数据。通过WAL和Snapshot相结合的方式,etcd可以有效地进行数据存储和节点故障恢复等操作。

​ 既然已经在WAL实时存储了所有的变更,为什么还需要Snapshot呢?随着使用量的增加,WAL存储的数据会急剧增加,为了防止磁盘空间不足,etcd默认每一万条记录做一次Snapshot,经过Snapshot以后的WAL文件就可以删除。通过API可以查询的历史etcd操作默认为一千条。

​ WAL最大的作用是记录了整个数据变化的全部历程。在etcd中,所有数据的修改在提交前,都要先写入到WAL中。使用WAL进行数据的存储使etcd拥有如下两个重要功能。

  • 故障快速恢复。当数据遭到破坏时,就可以通过执行所有WAL中记录的修改操作,快速从最原始的数据恢复到数据损坏前的状态。
  • 数据回滚或重做。因为所有的修改操作都被记录在WAL中,在需要回滚或重做时,只需要反向或正向执行日志中的操作即可。

etcdAPI一览

​ etcd中处理API的包称为Store,顾名思义,Store模块就像一个商店一样把etcd已经准备好的各项底层支持加工起来,为用户提供各式各样的API支持,处理用户的各项请求。要理解Store,就要从etcd的API入手。打开etcd的API列表,我们可以看到如下API,它们都是对etcd存储的键值进行的操作,亦即Store提供的内容。API中提到的目录(directory)和键(key ),上文中有时也称为etcd节点。

为etcd存储的键赋值:

curl http://127.0.0.1:2379/v2/keys/message -XPUT -d value="Hello world"
{
    "action":"set",
    "node":{
        "createdIndex":2,
        "key":"/message",
        "modifiedIndex":2,
          "value":"Hello world"
    }
}

反馈的内容含义如下:

  • action:刚刚进行的动作名称。
  • node.key:请求的HTTP路径。etcd使用一个类似文件系统的方式来反映键值存储的内容。
  • node.value:刚刚请求的键所存储的内容。
  • node.createdIndex: etcd节点每次发生变化时,该值会自动增加。除了用户请求外,etcd内部运行(如启动、集群信息变化等)也可能会因为节点有变动而引起该值的变化。
  • node.modifiedIndex:类似node.createdIndex,能引起该值变化的操作包括set ,delete, update, create、compareAndSwap或compareAndDelete。

查询etcd某个键存储的值:

curl http://127.0.0.1:2379/v2/keys/message

修改键值与创建新值几乎相同,但是反馈时会有一个prevNode值反映修改前存储的内容:

curl http://127.0.0.1:2379/v2/keys/message -XPUT -d value="Hello etcd"

删除一个值:

curl http://127.0.0.1:2379/v2/keys/message -XDELETE

对一个键进行定时删除。etcd中对键进行定时删除,设定一个ttl值,当这个值到期时,键就会被删除。反馈的内容会给出expiration项告知超时时间,以及ttl项告知设定的时长。

curl http://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -d ttl=5

取消定时删除任务:

curl http://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -d ttl= -d prevExist=true

对键值修改进行监控。etcd提供的这个API让用户可以监控一个值或者递归式地监控一个目录及其子目录的值,当目录或值发生变化时,etcd会主动通知。

curl http://127.0.0.1:2379/v2/keys/foo?wait=true

对过去的键值操作进行查询。类似上面提到的监控,在其基础上指定过去某次修改的索引编号,就可以查询历史操作。默认可查询的历史记录为一千条。

curl 'http://127.0.0.1:2379/v2/keys/foo?wait=true&waitIndex=7'

自动在目录下创建有序键。在对创建的目录使用POS丁参数时,会自动在该目录下创建一个以createdIndex值为键的值,这样就相当于根据创建时间的先后进行了严格排序。该API对分布式队列这类场景非常有用。

curl http://127.0.0.1:2379/v2/keys/queue -XPOST -d value=Job1
{
    "action":"create",
    "node":{
        "createdIndex":6,
        "key":"/queue/6",
        "modifiedIndex":6,
          "value":"Job1"
    }
}

按顺序列出所有创建的有序键:

curl -s 'http://127.0.0.1:2379/v2/keys/queue?recursive=true&sorted=true'

创建定时删除的目录与定时删除某个键类似。如果目录因为超时被删除了,其下的所有内容也会自动超时删除:

curl http://127.0.0.1:2379/v2/keys/dir -XPUT -d ttl=30 -d dir=true

刷新超时时间:

curl http://127.0.0.1:2379/v2/keys/dir -XPUT -d ttl=30 -d dir=true -d prevExist=true

自动化CAS ( Compare-and-Swap)操作。etcd强一致性最直观的表现就是这个API,通过设定条件,阻止节点二次创建或修改。即当且仅当CAS的条件成立,用户的指令被执行。条件有以下几个:

  • prevValue:表示先前节点的值,如果值与提供的值相同才允许操作。
  • prevIndex:表示先前节点的编号,如果编号与提供的校验编号相同才允许操作。
  • prevExist:判断先前节点是否存在,如果存在则不允许操作。这个常常被用于分布式锁的唯一获取。

创建目录:

curl http://127.0.0.1:2379/v2/keys/dir -XPUT -d dir=true

列出目录下所有的节点信息,最后以“/”结尾,还可以通过recursive参数递归列出所有子目录的信息。

curl http://127.0.0.1:2379/v2/keys/

删除目录。默认情况下只允许删除空目录,如果要删除有内容的目录需要加上recursive=true参数。

curl  'http://127.0.0.1:2379/v2/keys/foo_dir?dir=true' -XDELETE

创建一个隐藏节点。命名时名字以下划线“_”开头,默认为隐藏键。

curl http://127.0.0.1:2379/v2/keys/_message -XPUT -d value="Hello hidden world"

猜你喜欢

转载自blog.csdn.net/fy_long/article/details/88662616
今日推荐