我要投搞

标签云

收藏小站

爱尚经典语录、名言、句子、散文、日志、唯美图片

当前位置:2019年全年资料内部公开36码 > 强一致性 >

基于hashicorpraft的分布式一致性实战教学

归档日期:08-15       文本归类:强一致性      文章编辑:爱尚语录

  导语:hashicorp/raft是raft算法的一种比较流行的golang实现,基于它能够比较方便的构建具有强一致性的分布式系统。本文通过实现一个简单的分布式缓存系统来介绍使用hashicorp/raft来构建分布式应用程序的方法。

  对于后台开发来说,随着业务的发展,由于访问量增大的压力和数据容灾的需要,一定会需要使用分布式的系统,而分布式势必会引入一致性的问题。

  一般把一致性分为三种类型:弱一致性、最终一致性、强一致性。这三种模型的一致性强度逐渐递增,实现代价也越来越大。通常弱一致性和最终一致性可以异步冗余,强一致性则是同步冗余,而同步也就意味着影响性能。

  对常见的互联网业务来说,使用弱一致性或者最终一致性即可。而使用强一致性一方面会影响系统的性能,另一方面实现也比较困难。常见的一致性协议如zab、raft、paxos,如果由业务纯自己来实现的话代价较大,而且很可能会因为考虑不周而引入其他问题。

  对于一些需要强一致性,而又希望花费较小代价的业务来说,使用开源的一致性协议实现组件会是个不错的选择。hashicorp/raft是raft协议的一种golang实现,由hashicorp公司实现并开源,已经在consul等软件中使用。它封装了raft协议的leader选举、log同步等底层实现,基于它能够相对比较容易的构建强一致性的分布式系统,下面以实现一个简单的分布式缓存服务(取名叫stcache)来演示hashicorp/raft的具体使用,完整代码可以在github上下载。

  首先还是简单介绍下raft协议。这里不详细介绍raft协议,只是为了方便理解后面的hashicorp/raft的使用步骤而简单列举出raft的一点原理。具体的raft协议可以参考raft的官网,如果已经了解raft协议可以直接跳过这一节。

  raft是一种相对易于理解的一致性的协议。它属于leader-follower型的协议,有且只有一个leader,所有的事务请求都由leader处理,leader征求follower的意见,在集群内部达成一致,决定是否执行事务。当leader出现故障,集群中的follower会通过投票的方式选出一个新的leader,维持集群运行。

  首先我们创建一个单机版本的stcache,它是一个简单的缓存服务器,在服务内部用一个map来保存数据,只提供简单的get和set操作。

  然后stcache开启一个http服务,提供两个api,第一个是set接口,用于设置数据到缓存,成功时返回ok,失败返回错误信息:

  下面我们在单机版stcache的基础上逐步扩充,让它成为一个具有强一致性的分布式系统。

  hashicorp/raft库提供NewRaft方法来创建一个raft节点,这也是使用这个库的最重要的一个api。NewRaft需要调用层提供6个参数,分别是:

  config是节点的配置信息,我们直接使用raft默认的配置,然后用监听的地址来作为节点的id。config里面还有一些可配置的项,后面我们用到的时候再说。

  SnapshotStore用来存储快照信息,对于stcache来说,就是存储当前的所有的kv数据,hashicorp内部提供3中快照存储方式,分别是:

  这里我们使用文件持久化存储。snapshotStore只是提供了一个快照存储的介质,还需要应用程序提供快照生成的方式,后面我们再具体说。

  Transport是raft集群内部节点之间的通信渠道,节点之间需要通过这个通道来进行日志同步、leader选举等。hashicorp/raft内部提供了两种方式来实现,一种是通过TCPTransport,基于tcp,可以跨机器跨网络通信;另一种是InmemTransport,不走网络,在内存里面通过channel来通信。显然一般情况下都使用TCPTransport即可,在stcache里也采用tcp的方式。

  第一个是Apply,当raft内部commit了一个log entry后,会记录在上面说过的logStore里面,被commit的log entry需要被执行,就stcache来说,执行log entry就是把数据写入缓存,即执行set操作。我们改造doSet方法, 这里不再直接写缓存,而是调用raft的Apply方式,为这次set操作生成一个log entry,这里面会根据raft的内部协议,在各个节点之间进行通信协作,确保最后这条log 会在整个集群的节点里面提交或者失败。

  FSM需要提供的另外两个方法是Snapshot()和Restore(),分别用于生成一个快照结构和根据快照恢复数据。首先我们需要定义快照,hashicorp/raft内部定义了快照的interface,需要实现两个func,Persist用来生成快照数据,一般只需要实现它即可;Release则是快照处理完成后的回调,不需要的话可以实现为空函数。

  我们定义一个简单的snapshot结构,在Persist里面,自己把缓存里面的数据用json格式化的方式来生成快照,sink.Write就是把快照写入snapStore,我们刚才定义的是FileSnapshotStore,所以会把数据写入文件。

  而快照生成和保存的触发条件除了应用程序主动触发外,还可以在Config里面设置SnapshotInterval和SnapshotThreshold,前者指每间隔多久生成一次快照,后者指每commit多少log entry后生成一次快照。需要两个条件同时满足才会生成和保存一次快照,默认config里面配置的条件比较高,我们可以自己修改配置,比如在stcache里面配置SnapshotInterval为20s,SnapshotThreshold为2,表示当满足距离上次快照保存超过20s,且log增加2条的时候,保存一个新的快照。

  服务重启的时候,会先读取本地的快照来恢复数据,在FSM里面定义的Restore函数会被调用,这里我们就简单的对数据解析json反序列化然后写入内存即可。至此,我们已经能够正常的保存快照,也能在重启的时候从文件恢复快照数据。

  集群最开始的时候只有一个节点,我们让第一个节点通过bootstrap的方式启动,它启动后成为leader。

  后续的节点启动的时候需要加入集群,启动的时候指定第一个节点的地址,并发送请求加入集群,这里我们定义成直接通过http请求。

  先启动的节点收到请求后,获取对方的地址(指raft集群内部通信的tcp地址),然后调用AddVoter把这个节点加入到集群即可。申请加入的节点会进入follower状态,这以后集群节点之间就可以正常通信,leader也会把数据同步给follower。

  当集群的leader故障后,集群的其他节点能够感知到,并申请成为leader,在各个follower中进行投票,最后选取出一个新的leader。leader选举是属于raft协议的内容,不需要应用程序操心,但是对有些场景而言,应用程序需要感知leader状态,比如对stcache而言,理论上只有leader才能处理set请求来写数据,follower应该只能处理get请求查询数据。为了模拟说明这个情况,我们在stcache里面我们设置一个写标志位,当本节点是leader的时候标识位置true,可以处理set请求,否则标识位为false,不能处理set请求。

  当故障切换的时候,follower变成了leader,应用程序如何感知到呢? 在raft结构里面提供有一个eaderCh,它是bool类型的channel,不带缓存,当本节点的leader状态有变化的时候,会往这个channel里面写数据,但是由于不带缓冲且写数据的协程不会阻塞在这里,有可能会写入失败,没有及时变更状态,所以使用leaderCh的可靠性不能保证。好在raft Config里面提供了另一个channel NotifyCh,它是带缓存的,当leader状态变化时会往这个chan写数据,写入的变更消息能够缓存在channel里面,应用程序能够通过它获取到最新的状态变化。

  我们首先在初始化config时候创建一个带缓存的chan,把它赋值给config里面的NotifyCh,然后在节点启动后监听这个chan,当本节点的leader状态变化时(变成leader或者从leader变成follower),就能够从这个chan里面读取到bool值,并调整我们先前设置的写标志位,控制是否能否处理set操作。

  做完上面的工作后,我们来测试下效果,我们同一台机器上启动3个节点来构成一个集群,第一个节点用bootstrapt的方式启动,成为leader

  这时候能在两个follower上看见apply的日志,follower节点写入了log,并收到leader的通知提交数据。

  通过查询接口,也能从follower里面查询到刚才写入的数据,证明数据同步没有问题。

  有一点需要说明的事,我们这里从follower是可能读不到最新数据的。由于leader对set操作返回的时候,follower可能还没有apply数据,所以从follower的get查询可能返回旧数据或者空数据。如果要保证能从follower查询到的一定是最新的数据还需要很多额外的工作,即做到linearizable read,有兴趣可以看这篇测试文章,这里不再展开。

  在指定的目录下面,能看见快照的具体信息,有两个文件,meta.json保存了版本号、log序号、集群节点地址等集群信息;state.bin里面是快照数据,这里就是我们刚刚写入的数据被json序列化后的字符串。

  现在把节点都停止,然后重新启动leader,内存的数据都丢失,它会从保存的快照文件里面恢复数据。重启follower也一样会从自己保存的快照里面加载数据。

  我们停掉leader节点,两个follower会开始选举,这里node2赢得了选举,进入leader状态,并且它开始打开set操作

  我们再请求node2监听的127.0.0.1:6001,发现已经可以正常写入数据了,leader切换顺利完成。

  我们再重启原来的leader节点,它会变成follower,并从新的leader(也就是node2)这里同步它所缺失的数据。

  上面所创建的stcache只是一个简单的示例程序,真正要做到在线上使用还有很多问题需要考虑,目前基于hashicorp/raft比较成熟的开源软件有consul,如果有兴趣可以通过它做进一步研究。

  总的来说,hashicorp/raft封装了raft的内部协议,提供简洁明了的使用方法,基于它能够很快速地构建出具有强一致性的应用程序。

  获取更多新鲜技术干货,可以关注我们腾讯云技术社区-云加社区官方号及知乎机构号

  Raft 是用来管理复制日志(replicated log)的一致性协议。它跟 multi-Paxos 作用相同,效率也相当,但是它的组织结构跟 Paxos 不同。这使得 Raft 比 Paxos 更容易理解并且更容易在工程实践中实现。为了使 Raft 协议更易懂,Raft...

  最近看了Ongaro在2014年的博士论文《CONSENSUS: BRIDGING THEORY AND PRACTICE》的部分章节,对raft有了初步的理解。其中论文中提到用于教学的user study,个人感觉非常不错,言简意赅,特此分享出来。本文基本与原讲解一致,又...

  最近看了Ongaro在2014年的博士论文《CONSENSUS: BRIDGING THEORY AND PRACTICE》的部分章节,对raft有了初步的理解。其中论文中提到用于教学的user study,个人感觉非常不错,言简意赅,特此分享出来。本文基本与原讲解一致,又...

  分布式系统基础-Raft算法 注意:这篇文章是我在阅读Raft算法的论文之后的一些想法,记录下来.其中可能有些地方表述的不准确,也可能我理解有误.所以请各位还是先阅读Raft算法论文 然后再读这篇文章.防止我误人子弟. 简介 学习分布式系统的朋友应该都知道,分布式一致性...

  一致性问题可以算是分布式领域的一个圣殿级问题了,关于它的研究可以回溯到几十年前。 拜占庭将军问题 Leslie Lamport 在三十多年前发表的论文《拜占庭将军问题》(参考[1])。 拜占庭位于如今的土耳其的伊斯坦布尔,是东罗马帝国的首都。由于当时拜占庭罗马帝国国土辽阔,...

  最近看的文章好像都是关于言行品质方面的,可见,做事先做人,做人先学会讲话。讲话有多重含义,讲话的好坏影响着事情的结果。经常要想一下 良言一句三冬暖,恶语伤人六月寒,多关注别人的感受,不要这么自私。 关于一言力作者给出了三个观点,一个是概括力,一个是短答力,一个是口号力。 我...

  你得明白,每个人都是屁股决定脑袋。 一、我地铁中的叛变 今天想讲一点关于“屁股”的事。 从我每天的挤地铁故事开始吧。 对不起,放错了,是这个。 因为我是起床困难户,所以每天坐地铁都避不开人最多的早高峰——顶级挤模式——大家男男女女贴腿肚皮级。 于是,当列车停靠,就会看到一种...

  I-场景 1. 哲学 机制而不是策略,自由放纵注意风格,产生了多样性。比如Unix应用程序提供很多的行为选项,让非技术的用户晕头转向,而失去了很多用户;但是策略相对短寿、机制才会长存,具有很大的灵活性。 趣味性是一个峰值效率的标志。对于程序员和开发人员来说,如果完成某项任务...

  “他人笑我太疯癫,我笑他人看不穿。” 万宁是我的一位特殊同学。 新学期两个班级要合在一起,我才与久仰大名的他接触。 更巧的是我们居然是同桌,他是经常是学校演讲的第一,是那种很会煲一些“鸡汤”的人,仿佛在他的人生里,什么都不算事,即使他是个残疾人。 他的双腿因为小儿麻痹症,往...

  保罗·里格比说到:“它被列为世界遗产,却没人晓得搭建他的原因,你无法否认这个史前遗迹的魅力......” 巨石阵,英国最神秘的考古发现,中古世界七大奇迹之一,欧洲著名的史前文化遗址。这些高大上的名号吸引着成千上万的游客前往观摩。不过多数去过的人会说:“就是一堆破石头,除了石...

本文链接:http://ravynhart.com/qiangyizhixing/498.html