etcd:
分布式高可用集群KV存储
etcd与Raft的关系:
Raft是强一致性的集群日志同步算法
etcd是一个分布式KV存储
etcd利用raft算法在集群中同步key-value
quorum模型:
集群需要2N+1个节点
replication:日志在leader生成,向follower复制,达到各个节点的日志序列最终一致
term:任期,重新选举产生的leader,其term单调递增
log index:日志行在日志序列的下标
交互协议:
etcd采用的HTTP+JSON协议,性能低效
SDK内置GRPC协议,性能高效
重要特性:
底层存储室按key有序排列的,可以顺序遍历
因为key有序,所以etcd天然支持按目录结构高效遍历
支持事物,提供类似if...then...esle...的食物能力
基于租约机制实现key的TTL过期
Key有序存储:
存储引擎室按key有序排列的:
lease租约:
实现一个key的自动过期
代码实现:
package main
import (
"fmt"
"go.etcd.io/etcd/clientv3"
"golang.org/x/net/context"
"time"
)
//分布式事务的乐观锁
func main() {
var (
conf clientv3.Config
client *clientv3.Client
kv clientv3.KV
lease clientv3.Lease
leaseGrantResp *clientv3.LeaseGrantResponse
leaseId clientv3.LeaseID
LeaseKeepAliveResponse <-chan *clientv3.LeaseKeepAliveResponse
leaseResp *clientv3.LeaseKeepAliveResponse
ctx context.Context
CancelFunc context.CancelFunc
txn clientv3.Txn
txnResp *clientv3.TxnResponse
err error
)
conf = clientv3.Config{
Endpoints: []string{"127.0.0.1:2379"},
DialTimeout: 5 * time.Second,
}
//链接
if client, err = clientv3.New(conf); err != nil {
fmt.Println("连接失败,Error:", err)
return
}
//创建KV
//kv = clientv3.NewKV(client)
//lease实现自动过期
//op操作
//txn事务:if else then
//1、上锁(创建租约,自动续租,拿着租约去抢占一个KEY)
lease = clientv3.NewLease(client) //创建一个租约
if leaseGrantResp, err = lease.Grant(context.TODO(), 5); err != nil { //申请10秒的租约
fmt.Println(err)
return
}
//拿到租约id
leaseId = leaseGrantResp.ID
//准备一个用于取消自动续租的context
ctx, CancelFunc = context.WithCancel(context.TODO())
defer CancelFunc() //确保程序退出后,自动续租停止
defer lease.Revoke(ctx, leaseId) //立即销毁租约s
if LeaseKeepAliveResponse, err = lease.KeepAlive(ctx, leaseId); err != nil { //自动续约
fmt.Println(err)
return
}
go func() { //处理续租
for {
select {
case leaseResp = <-LeaseKeepAliveResponse:
if LeaseKeepAliveResponse == nil {
fmt.Println("租约到期")
goto END
} else {
fmt.Println("收到自动续租应答:", leaseResp.ID)
}
}
}
END:
}()
//抢key
//if不存在key,then设置它,esle强锁失败
kv = clientv3.NewKV(client)
txn = kv.Txn(context.TODO()) //创建事务
txn.If(clientv3.Compare(clientv3.CreateRevision("/cron/lock/job9"), "=", 0)).
Then(clientv3.OpPut("/cron/lock/job9", "job9", clientv3.WithLease(leaseId))).
Else(clientv3.OpGet("/cron/lock/job9"))
if txnResp, err = txn.Commit(); err != nil {
fmt.Println(err)
return
}
//判断是否抢到了锁
if !txnResp.Succeeded {
fmt.Println("锁被占用:", string(txnResp.Responses[0].GetResponseRange().Kvs[0].Value))
return
}
//2、处理业务
fmt.Println("处理任务")
time.Sleep(10 * time.Second)
//3、释放锁(取消自动续租,释放租约):defer会把租约释放掉,关联的KV就被删除了
}