GoGolang 入门资料+笔记深入浅出golang

raft理论与实践[6]-lab3a-基于raft构建分布式容错

2020-02-15  本文已影响0人  唯识相链2

准备工作

前言

本服务实现的功能

实验思路

获取源代码

<pre style="font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; -webkit-font-smoothing: antialiased; margin: 0px; padding: 0.88889em; max-width: 100%; font-size: 0.9em; word-break: break-all; white-space: pre-line; font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.45; color: rgb(86, 116, 130); word-wrap: normal; background: rgb(246, 246, 246); overflow: auto; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">git clone git@github.com:dreamerjackson/golang-deep-distributed-lab.git
git reset --hard d345b34bc</pre>

客户端

补充Clerk

<pre style="font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; -webkit-font-smoothing: antialiased; margin: 0px; padding: 0.88889em; max-width: 100%; font-size: 0.9em; word-break: break-all; white-space: pre-line; font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.45; color: rgb(86, 116, 130); word-wrap: normal; background: rgb(246, 246, 246); overflow: auto; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">type Clerk struct {
...
leader int // remember last leader
seq int // RPC sequence number
id int64 // client id
}</pre>

补充Get方法

<pre style="font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; -webkit-font-smoothing: antialiased; margin: 0px; padding: 0.88889em; max-width: 100%; font-size: 0.9em; word-break: break-all; white-space: pre-line; font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.45; color: rgb(86, 116, 130); word-wrap: normal; background: rgb(246, 246, 246); overflow: auto; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">func (ck *Clerk) Get(key string) string {
DPrintf("Clerk: Get: %q\n", key)
cnt := len(ck.servers)
for {
args := &GetArgs{Key: key, ClientID: ck.id, SeqNo: ck.seq}
reply := new(GetReply)

    ck.leader %= cnt
    done := make(chan bool, 1)
    go func() {
        ok := ck.servers[ck.leader].Call("KVServer.Get", args, reply)
        done <- ok
    }()
    select {
    case <-time.After(200 * time.Millisecond): // rpc timeout: 200ms
        ck.leader++
        continue
    case ok := <-done:
        if ok && !reply.WrongLeader {
            ck.seq++
            if reply.Err == OK {
                return reply.Value
            }
            return ""
        }
        ck.leader++
    }
}

return ""

}</pre>

补充Append和Put方法

<pre style="font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; -webkit-font-smoothing: antialiased; margin: 0px; padding: 0.88889em; max-width: 100%; font-size: 0.9em; word-break: break-all; white-space: pre-line; font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.45; color: rgb(86, 116, 130); word-wrap: normal; background: rgb(246, 246, 246); overflow: auto; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">func (ck *Clerk) Put(key string, value string) {
ck.PutAppend(key, value, "Put")
}
func (ck *Clerk) Append(key string, value string) {
ck.PutAppend(key, value, "Append")
}</pre>

<pre style="font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; -webkit-font-smoothing: antialiased; margin: 0px; padding: 0.88889em; max-width: 100%; font-size: 0.9em; word-break: break-all; white-space: pre-line; font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.45; color: rgb(86, 116, 130); word-wrap: normal; background: rgb(246, 246, 246); overflow: auto; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">func (ck *Clerk) PutAppend(key string, value string, op string) {
// You will have to modify this function.
DPrintf("Clerk: PutAppend: %q => (%q,%q) from: %d\n", op, key, value, ck.id)
cnt := len(ck.servers)
for {
args := &PutAppendArgs{Key: key, Value: value, Op: op, ClientID: ck.id, SeqNo: ck.seq}
reply := new(PutAppendReply)

    ck.leader %= cnt
    done := make(chan bool, 1)
    go func() {
        ok := ck.servers[ck.leader].Call("KVServer.PutAppend", args, reply)
        done <- ok
    }()
    select {
    case <-time.After(200 * time.Millisecond): // rpc timeout: 200ms
        ck.leader++
        continue
    case ok := <-done:
        if ok && !reply.WrongLeader && reply.Err == OK {
            ck.seq++
            return
        }
        ck.leader++
    }
}

}</pre>

Server

<pre style="font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; -webkit-font-smoothing: antialiased; margin: 0px; padding: 0.88889em; max-width: 100%; font-size: 0.9em; word-break: break-all; white-space: pre-line; font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.45; color: rgb(86, 116, 130); word-wrap: normal; background: rgb(246, 246, 246); overflow: auto; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">type KVServer struct {
...
rf *raft.Raft
applyCh chan raft.ApplyMsg
// Your definitions here.
persist raft.Persister
db map[string]string
notifyChs map[int]chan struct{} // per log
// duplication detection table
duplicate map[int64]
LatestReply
}</pre>

完成PutAppend、Get方法

<pre style="font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; -webkit-font-smoothing: antialiased; margin: 0px; padding: 0.88889em; max-width: 100%; font-size: 0.9em; word-break: break-all; white-space: pre-line; font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.45; color: rgb(86, 116, 130); word-wrap: normal; background: rgb(246, 246, 246); overflow: auto; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">func (kv *KVServer) PutAppend(args *PutAppendArgs, reply *PutAppendReply) {
// Your code here.
// not leader
if _, isLeader := kv.rf.GetState(); !isLeader {
reply.WrongLeader = true
reply.Err = ""
return
}

DPrintf("[%d]: leader %d receive rpc: PutAppend(%q => (%q,%q), (%d-%d).\n", kv.me, kv.me,
    args.Op, args.Key, args.Value, args.ClientID, args.SeqNo)

kv.mu.Lock()
// duplicate put/append request
if dup, ok := kv.duplicate[args.ClientID]; ok {
    // filter duplicate
    if args.SeqNo <= dup.Seq {
        kv.mu.Unlock()
        reply.WrongLeader = false
        reply.Err = OK
        return
    }
}

// new request
cmd := Op{Key: args.Key, Value: args.Value, Op: args.Op, ClientID: args.ClientID, SeqNo: args.SeqNo}
index, term, _ := kv.rf.Start(cmd)
ch := make(chan struct{})
kv.notifyChs[index] = ch
kv.mu.Unlock()

reply.WrongLeader = false
reply.Err = OK

// wait for Raft to complete agreement
select {
case <-ch:
    // lose leadership
    curTerm, isLeader := kv.rf.GetState()
    if !isLeader || term != curTerm {
        reply.WrongLeader = true
        reply.Err = ""
        return
    }
case <-kv.shutdownCh:
    return
}

}</pre>

完成对于log的应用操作

<pre style="font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; -webkit-font-smoothing: antialiased; margin: 0px; padding: 0.88889em; max-width: 100%; font-size: 0.9em; word-break: break-all; white-space: pre-line; font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.45; color: rgb(86, 116, 130); word-wrap: normal; background: rgb(246, 246, 246); overflow: auto; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">func (kv *KVServer) applyDaemon() {
for {
select {
case msg, ok := <-kv.applyCh:
if ok {
// have client's request? must filter duplicate command
if msg.Command != nil {
cmd := msg.Command.(Op)
kv.mu.Lock()
if dup, ok := kv.duplicate[cmd.ClientID]; !ok || dup.Seq < cmd.SeqNo {
switch cmd.Op {
case "Get":
kv.duplicate[cmd.ClientID] = &LatestReply{Seq: cmd.SeqNo,
Reply: GetReply{Value: kv.db[cmd.Key],}}
case "Put":
kv.db[cmd.Key] = cmd.Value
kv.duplicate[cmd.ClientID] = &LatestReply{Seq: cmd.SeqNo,}
case "Append":
kv.db[cmd.Key] += cmd.Value
kv.duplicate[cmd.ClientID] = &LatestReply{Seq: cmd.SeqNo,}
default:
DPrintf("[%d]: server %d receive invalid cmd: %v\n", kv.me, kv.me, cmd)
panic("invalid command operation")
}
if ok {
DPrintf("[%d]: server %d apply index: %d, cmd: %v (client: %d, dup seq: %d < %d)\n",
kv.me, kv.me, msg.CommandIndex, cmd, cmd.ClientID, dup.Seq, cmd.SeqNo)
}
}
// notify channel
if notifyCh, ok := kv.notifyChs[msg.CommandIndex]; ok && notifyCh != nil {
close(notifyCh)
delete(kv.notifyChs, msg.CommandIndex)
}
kv.mu.Unlock()
}
}
}
}
}</pre>

测试

<pre style="font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; -webkit-font-smoothing: antialiased; margin: 0px; padding: 0.88889em; max-width: 100%; font-size: 0.9em; word-break: break-all; white-space: pre-line; font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.45; color: rgb(86, 116, 130); word-wrap: normal; background: rgb(246, 246, 246); overflow: auto; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">> go test -v -run=3A</pre>

<pre style="font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; -webkit-font-smoothing: antialiased; margin: 0px; padding: 0.88889em; max-width: 100%; font-size: 0.9em; word-break: break-all; white-space: pre-line; font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.45; color: rgb(86, 116, 130); word-wrap: normal; background: rgb(246, 246, 246); overflow: auto; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">rm -rf res
mkdir res
set int j = 0
for ((i = 0; i < 2; i++))
do
for ((c = ((i*10)); c <(( (i+1)*10)); c++))
do
(go test -v -run TestPersistPartitionUnreliableLinearizable3A) &> ./res/$c &
done

sleep 40

if grep -nr "FAIL.*raft.*" res; then
    echo "fail"
fi

done</pre>

总结

参考资料

上一篇 下一篇

猜你喜欢

热点阅读