每日一Go-71、理论知识:CAP 、一致性原理 、Raft 机制(简化实现一个 Raft)

在分布式系统开发中,Go 语言因其原生支持并发(Goroutine 和 Channel)而成为实现共识算法的首选语言。

要理解 Raft,我们得先从宏 观的理论基础开始。

一、理论基石:CAP 定理

CAP 定理指出,在一个分布式系统中,以下三个特性不可兼得,最多只能同时满足两个:

  • Consistency(一致性): 每次读取都能读到最新写入的数据。

  • Availability(可用性): 每次请求都能得到响应(不保证是最新的)。

  • Partition Tolerance(分区容错性): 即使节点之间的网络断开(分区),系统仍能运行。

在分布式系统中,P 是必须保证的。 因此,我们通常在 CP(侧重强一致性,如 Etcd/Consul)和 AP(侧重可用性,如 Cassandra/Eureka)之间做权衡。Raft 属于 CP 协议。

二、一致性原理

一致性指的是系统中所有副本的数据状态相同。分布式系统实现一致性通常靠 复制协议,比如:

  1. 主从复制(Leader + Follower)

  2. 共识算法(Consensus Algorithm):保证即使部分节点宕机,系统仍能达成一致。

常用的共识算法:

  • Paxos:经典算法,但实现复杂。

  • Raft:理解简单,工程上更常用(etcd/k8s 用 Raft)。

三、什么是 Raft?

Raft 是一种为了管理复制日志的 共识算法 。它的设计目标是 易于理解 。它将一致性问题分解为三个子问题:

复制代码
1.  领导选举 (Leader Election):  当现有领导者失效时,选出新的领导者。

2.  日志复制 (Log Replication):  领导者接收客户端指令,并同步到其他节点。

3.  安全性 (Safety):  确保如果任何节点应用了某个日志条目,则其他节点不会在该索引处应用不同的条目。

四、简化实现一个Raft

  1. 定义节点状态
go 复制代码
type State int

const (
    Follower State = iota //追随者
    Candidate             //候选人
    Leader                // 领导
)

type Raft struct {
    mu        sync.Mutex
    me        int      // 节点 ID
    currentTerm int    // 当前任期
    votedFor    int    // 给谁投了票
    state       State  // 当前角色

    // 计时器
    electionTimer *time.Timer
}
  1. 选举逻辑(心跳与超时)
go 复制代码
func (rf *Raft) ticker() {
    for {
        select {
        case <-rf.electionTimer.C:
            rf.mu.Lock()
            // 超时了,变身为候选人发起投票
            rf.state = Candidate
            rf.currentTerm++
            rf.votedFor = rf.me
            rf.resetTimer() // 随机化超时时间,防止"选票瓜分"
            go rf.startElection()
            rf.mu.Unlock()
        }
    }
}
  1. 处理投票请求 (RequestVote)
cs 复制代码
func (rf *Raft) RequestVote(args *RequestVoteArgs, reply *RequestVoteReply) {
    rf.mu.Lock()
    defer rf.mu.Unlock()

    // 如果对方任期比我小,拒绝投票
    if args.Term < rf.currentTerm {
        reply.VoteGranted = false
        return
    }

    // 如果对方任期大,我变回 Follower
    if args.Term > rf.currentTerm {
        rf.currentTerm = args.Term
        rf.state = Follower
        rf.votedFor = -1
    }

    // 投票逻辑
    if rf.votedFor == -1 || rf.votedFor == args.CandidateId {
        rf.votedFor = args.CandidateId
        reply.VoteGranted = true
        rf.resetTimer() // 投了票,刷新自己的计时器
    }
}

五、为什么Go适合写Raft?

  • RPC 框架: Go 内置的 net/rpc 或 gRPC 可以非常方便地实现节点间的通信。

  • Select/Timer: Raft 大量依赖超时控制。Go 的 select 配合 time.Timer 能优雅地处理"竞态超时"。

  • 状态机: 借助于 Channel,可以很方便地将已提交的日志推送到应用层状态机。


友情链接:加班费计算器(vx小程序搜索"加班计")


*源码地址*

评论区要


如果您喜欢这篇文章,请您(点赞、分享、亮爱心),万分感谢!

相关推荐
薛之谦_12 分钟前
Java 全栈知识体系:从 IDEA 高效开发到 JVM、并发与 Stream 实战
java·jvm·intellij-idea
吴可可1231 小时前
WScript Shell操作注册表详解
开发语言
纪念 2292 小时前
C++ 算法基础 (差分)
开发语言·c++·算法
文丑颜不良啊4 小时前
LeetCode2761~2780题目解析
java·leetcode
IT枫斗者枫哥5 小时前
子线程断言失败,JUnit 为什么还显示通过?
java·后端
码农小河666 小时前
基于PHP的电商后台蜜罐系统
开发语言·php
starzy19906 小时前
深入理解Scala柯里化函数:函数链式变换
开发语言·scala
ShineWinsu7 小时前
对于Qt:QTreeWidget常见属性以及QTreeWidgetItem类的解析
开发语言·c++·qt·gui·客户端·多叉树·qtreewidget
落羽的落羽7 小时前
【C++】C++17的主要特性
linux·服务器·开发语言·c++·算法·机器学习
考虑考虑7 小时前
JDK27中的Set.ofLazy
java·后端·java ee