etcd

是配置共享和服务发现的键值存储系统
  • 1 应用程序可以在集群中共享信息、配置或作服务发现,etcd 会在集群的各个节点中复制这些数据并保证这些数据始终正确。

  • 2 服务注册的场景在微服务架构设计中,被用来统一管理各个服务的服务地址,客户端和网关可以通过注册中心查询要访问的目标服务地址,实现动态服务访问,并在此基础上实现服务负 载均衡。

  • 3 有点类似Redis的操作,首先会启动一个服务,然后利用命令行来操作,它是一个KV存储的格式

1 get  put watch del 等命令!
  • 4 watch 是来监控相关的操作的
  • 5 etcd 存储了历史信息,也就是有个机制缓存了,这是基于B+树来执行的。(B+是随机顺序的!)
  • 6 etcd 里的Leaded 会为每个Key创建一个索引,每个索引对应着一个B+树
  • 7 每个B+树里存储了Key的历史版本信息。
  • 8
etcd 实现强一致性的核心在于 Raft算法
  • 1 服务器下载地址:https://github.com/etcd-io/etcd/releases/tag/v3.5.5 下载完毕直接启动etcd.exe 服务器就行。
  • 2 etcd 是一个k,v 存储系统
  • 3 put 命令是写数据:.\etcdctl.exe put key
  • 4 .\etcdctl.exe get key 是获取key的值
  • 5 .\etcdctl.exe watch key 是监听这个key
  • 6 .\etcdctl.exe txn -i 可以进入事务操作
{"header":{"cluster_id":14841639068965178418,"member_id":10276657743932975437,"revision":8,"raft_term":4},"kvs":[{"key":"a2V5","create_revision":5,"mod_revision":8,"version":4,"value":"eXV5dQ=="}],"count":1}
  • 7 revision 字段是Key的全局版本号,在内存中B树会维护一个Key-->对应的revison,而在磁盘中B+树会针对revison版本号来修改对应的value (映射关系!)
  • 8 .\etcdctl.exe lease grant xxx 设置一个过期时间
.\etcdctl.exe lease grant 50
.\etcdctl.exe lease put key sos
.\etcdctl.exe lease put key sos2 --lease=xxxxx字符串
.\etcdctl.exe get key   //等过期时间一过,在获取的时候就会失败!
etcd一致性
  • 1 先写日志在写磁盘,涉及到leader的选举机制,利用Raft共识算法达成数据同步。
demo
  • 1 wins本地启动服务器: etcd.exe

import (
    "context"
    "fmt"
    clientv3 "go.etcd.io/etcd/client/v3"
    "time"
)

func main() {
    client, err := clientv3.New(clientv3.Config{
        Endpoints:   []string{"localhost:2379"},
        DialTimeout: 5 * time.Second,
    })
    if err != nil {
        fmt.Println("connect to etcd is failed")
        return
    }
    defer client.Close()
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    res, _ := client.Get(ctx, "name")
    cancel()

    for index, v := range res.Kvs {
        fmt.Printf("下标为:%v 键是:%s 值是:%s \n", index, v.Key, v.Value)
    }
}
Raft.go
import ( 
 "fmt" 
 "math/rand" 
 "sync" 
 "time" 
) 

type NodeInfo struct { 
    ID string 
    Port string
} 
type Message struct {
    MsgBody string
    MsgID   int
}
type Raft struct {
    //本节点信息
    node *NodeInfo
    //本节点获得的投票数
    vote int
    //互斥锁
    lock sync.Mutex
    //本节点编号
    me string
    //当前任期
    currentTerm int
    //为哪个节点投票
    votedFor string
    //当前节点状态0 follower  1 candidate  2 leader
    state int
    //发送最后一条消息的时间
    lastSendMessageTime int64
    //发送最后一次心跳的时间
    lastSendHeartBeatTime int64
    //当前节点的领导
    currentLeader string
    //心跳超时时间(秒)
    heartBeatTimeout int
    //接收投票成功通道
    voteChan chan bool
    //心跳信号
    heartChan chan bool
}

func NewRaft(id, port string) *Raft {
    rf := new(Raft)
    rf.node = &NodeInfo{
        ID:   id,
        Port: port,
    }
    //当前节点获得票数
    rf.setVote(0)
    //编号
    rf.me = id
    //给0  1  2三个节点投票,给谁都不投
    rf.setVoteFor("-1")
    //设置节点状态 0 follower
    rf.setStatus(0)
    //最后一次心跳检测时间
    rf.lastSendHeartBeatTime = 0
    //心跳超时时间
    rf.heartBeatTimeout = heartBeatTimeout
    //最初没有领导
    rf.setCurrentLeader("-1")
    //设置任期
    rf.setTerm(0)
    //投票通道
    rf.voteChan = make(chan bool)
    //心跳通道
    rf.heartChan = make(chan bool)
    return rf
}
//设置投票数量
func (rf *Raft) setVote(num int) {
    rf.lock.Lock()
    rf.vote = num
    rf.lock.Unlock()
}

//设置为谁投票
func (rf *Raft) setVoteFor(id string) {
    rf.lock.Lock()
    rf.votedFor = id
    rf.lock.Unlock()
}

//设置当前节点状态
func (rf *Raft) setStatus(state int) {
    rf.lock.Lock()
    rf.state = state
    rf.lock.Unlock()
}

//设置当前领导者
func (rf *Raft) setCurrentLeader(leader string) {
    rf.lock.Lock()
    rf.currentLeader = leader
    rf.lock.Unlock()
}

//设置任期
func (rf *Raft) setTerm(term int) {
    rf.lock.Lock()
    rf.currentTerm = term
    rf.lock.Unlock()
}

//投票累加
func (rf *Raft) voteAdd() {
    rf.lock.Lock()
    rf.vote++
    rf.lock.Unlock()
}

//任期累加
func (rf *Raft) termAdd() {
    rf.lock.Lock()
    rf.currentTerm++
    rf.lock.Unlock()
}

//获取当前时间的毫秒数
func millisecond() int64 {
    return time.Now().UnixNano() / int64(time.Millisecond)
}

//产生随机值
func randRange(min, max int64) int64 {
    //用于心跳信号的时间
    rand.Seed(time.Now().UnixNano())
    return rand.Int63n(max-min) + min
}

//恢复默认设置
func (rf *Raft) reDefault() {
    rf.setVote(0)
    rf.setVoteFor("-1")
    rf.setStatus(0)
}

//给跟随者节点发送心跳包
func (rf *Raft) sendHeartPacket() {
    //如果收到通道开启的消息,将会向其他节点进行固定频率的心跳检测
    <-rf.heartChan //没有收到channel就会阻塞等待
    for {
        fmt.Println("本节点开始发送心跳检测")
        rf.broadcast("Raft.HeartBeatResponse", rf.node, func(ok bool) {
            fmt.Println("收到心跳检测", ok)
        })
        //最后一次心跳的时间
        rf.lastSendHeartBeatTime = millisecond()
        //休眠 --》心跳检测频率的时间
        time.Sleep(time.Second * time.Duration(heartBeatRate))
    }
}

//修改节点为候选人状态
func (rf *Raft) becomeCandidate() bool {
    r := randRange(1500, 5000)
    //休眠随机时间后,再开始成为候选人
    time.Sleep(time.Duration(r) * time.Millisecond)
    //如果当前节点是跟随者,并且没有领导,也没有为别人投票
    if rf.state == 0 && rf.currentLeader == "-1" && rf.votedFor == "-1" {
        //将节点状态变成候选者
        rf.setStatus(1)
        //设置为自己投了票
        rf.setVoteFor(rf.me)
        //自己的投票数量增加
        rf.voteAdd()
        //节点任期加1
        rf.termAdd()

        fmt.Println("本节点已经变成候选人状态")
        fmt.Printf("当前获得的票数:%d\n", rf.vote)
        //开启选举通道
        return true
    }
    return false
}

//进行选举
func (rf *Raft) election() bool {
    fmt.Println("开始进行领导者选举,向其他节点进行广播")
    go rf.broadcast("Raft.Vote", rf.node, func(ok bool) {
        rf.voteChan <- ok
    })
    for {
        select {
        //选举超时
        case <-time.After(time.Second * time.Duration(electionTimeout)):
            fmt.Println("领导者选举超时,节点变更为追随者状态")
            rf.reDefault()
            return false
        case ok := <-rf.voteChan:
            if ok {
                rf.voteAdd()
                fmt.Printf("获得来自其他节点的投票,当前得票数:%d\n", rf.vote)
            }
            if rf.vote >= nodeCount/2+1 && rf.currentLeader == "-1" {
                fmt.Println("获得大多数节点的同意,本节点被选举成为了leader")
                //节点状态变为2,代表leader
                rf.setStatus(2)
                //当前领导者为自己
                rf.setCurrentLeader(rf.me)
                fmt.Println("向其他节点进行广播本节点成为了leader...")
                go rf.broadcast("Raft.ConfirmationLeader", rf.node, func(ok bool) {
                    fmt.Println("其他节点:是否同意[", rf.node.ID, "]为领导者", ok)
                })
                //有leader了,可以发送心跳包了
                rf.heartChan <- true
                return true
            }
        }
    }
}

//尝试成为候选人并选举
func (rf *Raft) tryToBeCandidateWithElection() {
    for {
        //尝试成为候选人节点
        if rf.becomeCandidate() {
            //成为后选人节点后 向其他节点要选票来进行选举
            if rf.election() {
                break
            } else {
                continue //领导者选举超时,重新称为候选人进行选举
            }
        } else {
            //没有变成候选人,则退出
            //不是跟随者,或者有领导,或者为别人投票
            break
        }
    }
}

//心跳超时检测
func (rf *Raft) heartTimeoutDetection() {
    for {
        //0.5秒检测一次
        time.Sleep(time.Millisecond * 5000)
        //心跳超时
        if rf.lastSendHeartBeatTime != 0 && (millisecond()-rf.lastSendHeartBeatTime) > int64(rf.heartBeatTimeout*1000) {
            fmt.Printf("心跳检测超时,已超过%d秒\n", rf.heartBeatTimeout)
            fmt.Println("即将重新开启选举")
            rf.reDefault()
            rf.setCurrentLeader("-1")
            rf.lastSendHeartBeatTime = 0
            go rf.tryToBeCandidateWithElection()
        }
    }
}

你可能感兴趣的:(etcd)