raft相关:raft在处理用户请求超时的时候,如何避免重试的请求被多次应用?

假如返回请求失败,可是过后宕机的节点恢复,日志又被顺利的同步下去,这样在转账业务上,可能导致客户端再次进行请求造成多次转账,像这种场景应该如何处理呢?

摘自知乎:https://www.zhihu.com/question/278551592/answer/400962941


在Raft论文的6.3节,这个问题有详细讨论。

用普通后台术语就是幂等。Raft作者把这归为实现linearizable semantics所需要处理的一部分。Raft论文里,也给出了具体的通用解决办法。基本思路是:

  • 每个要做proposal的client需要一个唯一的identifier,它的每个不同proposal需要有一个顺序递增的序列号,client id和这个序列号由此可以唯一确定一个不同的proposal,从而使得各个raft节点可以记录保存各proposal应用以后的结果。
  • 当一个proposal超时,client不提高proposal的序列号,使用原proposal序列号重试。
  • 当一个proposal被成功提交并应用且被成功回复给client以后,client顺序提高proposal的序列号,并记录下收到的成功回复的proposal的序列号。raft节点收到一个proposal请求以后,得到请求中夹带的这个最大成功回复的proposal的序列号,它和它之前所有的应用结果都可以删去。proposal序列号和client id可用于判断这个proposal是否应用过,如果已经应用过,则不再再次应用,直接返回已保存的结果。等于是每个不同的proposal可以被commit多次,在log中出现多次,但永远只会被apply一次。
  • 系统维护一定数量允许的client数量,比如可以用LRU策略淘汰。请求过来了,而client已经被LRU淘汰掉了,则让client直接fail掉。
  • 这些已经注册的client信息,包括和这些client配套的上述proposal结果、各序列号等等,需要在raft组内一致的维护。也就是说,上述各raft端数据结构和它们的操作实际是state machine的一部分。在做snapshotting的时候,它们同样需要被保存与恢复。

可能感觉让这样重试的request被commit多次有奇怪。其实不奇怪,实际操作中,它们对状态机而言是个NO-OP。原文中的原话也清楚列明这些log entry会在raft log中重复出现,由状态机来负责过滤掉,状态机能看到接触到的自然是commit以后的log entry。

The Raft log provides a serial order in which commands are applied on every server. Commands take effect instantaneously and exactly once according to their first appearance in the Raft log, since any subsequent appearances are filtered out by the state machines as described above.

不是所有的应用都需要这样的功能。最直接的例子就是membership change本身。membership change的时候,比如有一个node的id是XYZ,因为超时你试图去再次提交一个membership remove操作,再次去删除这个id为XYZ的节点,它并不带来实际损害(很多raft库不允许一个已经被删除的节点再次以相同node id加入回来)。

给TA打赏
共{{data.count}}人
人已打赏
手机数码

云米互联网净水器小蓝调系列400G 小巧俏皮 6级过滤 采用智能水龙头

2021-4-26 14:41:41

默认分类

wordpress 7b2主题新增友情链接单页

2021-9-22 11:25:07

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索