raft论文翻译,raft论文作者

  

  很多同学会认为强一致存储的实现不能实现高性能,但事实并非如此。在合理的优化下,强一致存储不会对系统的吞吐量产生太大的影响。本文将谈谈这些优化技术。   

  

  就价值观达成一致的简单过程   

  

  领导收到一个请求。   

  

  领导者存储这个Raft日志,并将AppendEntries消息发送给追随者。   

  

  跟随者接收并存储这个Raft日志,并将AppendEntries消息回复给领导者。   

  

  领导者接收来自大多数追随者的响应,并向追随者发送AppendEntries消息以确认日志已被提交。   

  

  领导亲自应用了这个日志。   

  

  领导申请完成后,回复客户。   

  

  在收到提交的AppendEntries消息后,跟随者也开始应用这个日志。   

  

  如果这些都按顺序处理,性能可想而知。让我们来看看Elasticell中为Raft所做的一些优化工作。   

  

  附加日志并行化   

  

  在此步骤中,领导者可以并行存储AppendEntries消息和Raft日志。为什么?   

  

  如果领导者没有崩溃,则与顺序处理结果一致。   

  

  如果领导者崩溃,那么如果大于n/2 ^ 1的跟随者收到这个消息并追加成功,那么这个Raft日志肯定会被提交,新当选的领导者会回复客户端;否则,该Raft日志将不会被提交,并且客户端将超时/出错/或重试结果(参见实现)。   

  

  异步应用   

  

  日志一旦提交,就不会影响应用时的正确性,所以可以异步应用这个日志。   

  

  物理连接复用   

  

  当系统中有越来越多的raft组时,每个Raft组中的所有副本将成对链接。从物理的角度,最后我们会看到两台物理机(物理机、虚拟机、容器等)之间会有大量的TCP链接。),导致链接爆炸。Elasticell的方法:   

  

  利用链路复用技术,单个存储过程中的所有Raft-Group复用一条物理链路。   

  

  对Raft消息进行封装,并添加头(头中有Raft-Group的元信息),这样当商店收到Raft消息时,就可以知道这些消息属于哪个Raft-Group,从而驱动Raft。   

  

  分批流水线作业   

  

  批处理和流水线是实现高吞吐量一致性协议的两个重要优化。这些概念可以在论文1中找到。可以想象,虽然强一致性存储带来了额外的复制开销,但就像流动的河流一样,河水的吞吐量并不会因为这个开销而降低,因为水流本身就是批处理和流水线的典型体现。其实类似的功能X-Paxos在最近阿里反复提到的X-DB跨机房优化中已经实现了。我们来看看Raft的批处理和流水线是如何在Elasticell中达到类似的效果的。   

  

  批处理涉及到Elasticell的每个阶段,它可以提高系统的吞吐量(与延迟相矛盾)。Elasticell中的一个Raft-Group使用一个Goroutine来处理Raft事件,比如Step、Request、Raft Ready、Tick、Apply Result等等。   

  

  在提议阶段,收集上一次到这次之间的所有请求,合并同类型请求,提出一个减少Raft网络请求的提议。   

  

  在Raft就绪阶段,收集上一次和当前处理Raft事件之间的所有就绪信息,并将Leader节点批次写入Raft日志。   

  

  在Apply阶段,由于Apply是一个异步的过程,所以可以将同类型的操作组合起来应用(比如可以将多个Redis的Set操作组合成一个MSet操作),可以减少CGO调用。   

  

  Raft的Leader向Follower发送AppendEntries时,如果等最后一个AppendEntries返回后再发送下一个AppendEntries,性能会很差。所以我们需要做流水线来加快速度,不停的发送AppendEntries而不需要等待最后一个AppendEntries返回。   

  

  如果你想保证性能和正确性,你需要做以下两件事:   

  

  领导者和追随者之间的发送管道必须是有序的,以便追随者能够以有序的方式处理AppendEntries。   

  

  能够处理AppendEntries的丢失,比如连续发送索引为2、3、4的三条追加消息,其中3条消息丢失,跟随者收到2和4,那么领导者必须重新发送3和4条追加消息(因为4条消息会被跟随者丢弃)。   

  

  关于第二点,Etcd的库已经处理了。当跟随者收到附加消息时,它将检查它是否与收到的最后一个Raft日志相匹配。如果   

不匹配,就返回Reject消息,那么按照Raft协议,Leader收到这个Reject消息,就会从3(4-1)重试。

  

Elasticell的实现方式:

  

保证用于发送Raft消息的链接在每两个节点直接只有一个

  

把当前节点待发送的Raft消息按照对端节点的ID做简单的hash,放到不同的线程中去,由这些线程负责发送(线程的数量就相当于Pipelining的管道数)

  

这样就能保证每个Follower收到的Raft消息是有序的,并且每个Raft都只有一个Goroutine来处理Raft事件,这些消息能够保证被顺序的处理。

  

Batching和Pipelining的trade off

  

Batching能够提高系统的吞吐量(会带来系统Latency增大),Pipelining能够降低系统的Latency(也能在一定程度上提高吞吐量),这个2个优化在决策的时候是有冲突的(在Pipelining中发送下一个请求的时候,需要等多少的Batch Size,也许多等一会就回收集更多的请求),目前Elasticell采用的方式是在不影响Pipelining的前提下,尽可能多的收集2次Pipelining之间的请求Batching处理策略,显然这并不是一个最优的解决方案。

  

还没有做的优化

  

以上是Elasticell目前已经做的一些优化,还有一些是未来需要做的:

  

不使用RocksDB存储Raft Log,由于Raft Log和RocksDB的WAL存在功能重复的地方,这样就多了一次文件IO

  

Raft的heartbeat合并,当一个节点上的Raft-Group的很多的时候,heartbeat消息过多

  

Batching Apply的时候,当前节点上所有正在Apply的Raft-Group一起做Batching而不是在一个Raft-Group上做Batching

  

更高效的Batching和Pipelining模式,参考论文<1>

  

参考

  

<1> Tuning Paxos for high-throughput with batching and pipelining

相关文章