每日一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小程序搜索"加班计")


*源码地址*

评论区要


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

相关推荐
ZenosDoron1 小时前
malloc规范
java·开发语言
阿杰 AJie1 小时前
ExcelUtils样式相关工具
java·后端
Aotman_1 小时前
JavaScript数组对象中指定字段转换
java·开发语言·前端·javascript·vue.js·前端框架·es6
星河漫步Lu1 小时前
Anaconda搭建深度学习虚拟环境
开发语言·python·深度学习
skywalker_111 小时前
Maven速通
java·maven
garmin Chen1 小时前
Elasticsearch(4):Java Rest Client 搜索与聚合速查
java·分布式·elasticsearch
gCode Teacher 格码致知1 小时前
Python教学:十六进制编码的显示方法-由Deepseek产生
开发语言·python·算法
并不喜欢吃鱼1 小时前
从零开始 C++------ 十四【C++ 数据结构】unordered_map/unordered_set 全解析:从使用到底层模拟实现
开发语言·数据结构·c++
曾几何时`1 小时前
Go(一)Gin框架 和 GORM机制
开发语言·golang·gin