Skip to content

Commit c714a89

Browse files
authored
Merge pull request happyer#1 from xlsdg/master
docs(raft): modify format
2 parents 48fe6f0 + 956a4aa commit c714a89

File tree

5 files changed

+71
-103
lines changed

5 files changed

+71
-103
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ _testmain.go
2323
*.test
2424
*.prof
2525

26+
.DS_Store

README.md

Lines changed: 12 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
  MapReduce 是 Google 提出的一个软件架构,用于大规模数据集(大于1TB)的并行运算。简而言之,就是将任务切分成很小的任务然后一个一个区的执行最后汇总,这就像小时候我们老师经常教育我们一样,大事化小,小事化了(瞬间感觉那时候老师好言简意赅啊!!!)思想就这么一个思想,那么按照这个思想在现代软件定义一切的世界里面,我们怎么运用这样的方式来解决海量数据的处理,这篇就告诉你一个这样的一个简单的实现使用 Go 语言。
44

5-
## 上车了
5+
## 上车
66

77
  简单介绍一下几个概念:
88

@@ -14,7 +14,7 @@
1414

1515
  有了上面找出 10 个好学生的需求的时候,我们来想想怎么去实现它呢,很显然这个需求可能是校长在开会的时候提出来的,那么具体的实现就是每个年级组长是不是要把每个年级排名前 10 的学生找出来,然后年级组长的领导,将这些信息在汇总取出 前 10 的学生咯,那么具体的每个年级怎么做呢?同理,将每个班的前10名学生找出来,然后汇总到年级部门咯。
1616

17-
## 发车了
17+
## 发车
1818

1919
  基本概览和思路已经明白了,现在开始构建整个 MapReduce 框架了,首先我们明确一个思想就是,将任务划分成合适的大小,然后对其进行计算,然后将每一步计算的的结果,进行一个汇总合并的过程。那么这两个过程我们先分别定义为Map 和Reduce 过程。
2020

@@ -30,14 +30,14 @@
3030
3. 最后按照 KeyValue 里面的 Key 进行分区,将内容写入到文件里面,以便于后面的 Reduce 过程执行;
3131

3232
``` go
33-
func doMap(
34-
jobName string, // // the name of the MapReduce job
35-
mapTaskNumber int, // which map task this is
36-
inFile string,
37-
nReduce int, // the number of reduce task that will be run
38-
mapF func(file string, contents string) []KeyValue,
33+
func doMap(
34+
jobName string, // // the name of the MapReduce job
35+
mapTaskNumber int, // which map task this is
36+
inFile string,
37+
nReduce int, // the number of reduce task that will be run
38+
mapF func(file string, contents string) []KeyValue,
3939
) {
40-
40+
4141
//setp 1 read file
4242
contents, err := ioutil.ReadFile(inFile)
4343
if err != nil {
@@ -77,7 +77,6 @@ mapF func(file string, contents string) []KeyValue,
7777
log.Fatal("do map encoders ",err)
7878
}
7979
}
80-
8180
}
8281
```
8382

@@ -87,15 +86,14 @@ mapF func(file string, contents string) []KeyValue,
8786
2. 按照读取相同文件中的 Key 进新按照字典顺序进行排序;
8887
3. 遍历读取的 KeyValue,并且调用用户的 Reduce 方法,将计算的结果继续写入到文件中;
8988

90-
9189
``` go
92-
func doReduce(
90+
func doReduce(
9391
jobName string, // the name of the whole MapReduce job
9492
reduceTaskNumber int, // which reduce task this is
9593
nMap int, // the number of map tasks that were run ("M" in the paper)
9694
reduceF func(key string, values []string) string,
9795
) {
98-
96+
9997
// file.Close()
10098

10199
//setp 1,read map generator file ,same key merge put map[string][]string
@@ -133,7 +131,6 @@ mapF func(file string, contents string) []KeyValue,
133131
keys = append(keys, k)
134132
}
135133

136-
137134
//setp 2 sort by keys
138135
sort.Strings(keys)
139136

@@ -153,7 +150,6 @@ mapF func(file string, contents string) []KeyValue,
153150

154151
file.Close()
155152
}
156-
157153
```
158154

159155
  Merge 过程
@@ -205,10 +201,9 @@ func (mr *Master) schedule(phase jobPhase) {
205201
}
206202
fmt.Printf("Schedule: %v phase done\n", phase)
207203
}
208-
209204
```
210205

211-
## 到站了
206+
## 到站
212207

213208
- 运行测试:
214209

@@ -218,20 +213,6 @@ func (mr *Master) schedule(phase jobPhase) {
218213

219214
![测试结果](img/check.png)
220215

221-
222216
- 测试倒排结果:
223217

224218
![倒排索引结果](img/ii.png)
225-
226-
227-
228-
229-
230-
231-
232-
233-
234-
235-
236-
237-

src/.DS_Store

2 KB
Binary file not shown.

src/raft/README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# 老司机带你用 Go 语言实现分布式一致性协议 Raft
2+
3+
  随着大型网站的各种高并发访问、海量数据处理等场景越来越多,如何实现网站的高可用、易伸缩、可扩展、安全等目标就显得越来越重要。
4+
5+
  为了解决这样一系列问题,大型网站的架构也在不断发展。提高大型网站的高可用架构,不得不提的就是分布式。任何一个分布式系统都无法同时满足 Consistency(一致性),Availability(可用性),Partition tolerance(分区容错性)这三个基本需求,最多只能满足其中两项。 但是,一个分布式系统无论在 CAP 三者之间如何权衡,都无法彻底放弃一致性(Consistency),如果真的放弃一致性,那么就说明这个系统中的数据根本不可信,数据也就没有意义,那么这个系统也就没有任何价值可言。所以,无论如何,分布式系统的一致性问题都需要重点关注。
6+
7+
  Raft 适用于一个管理日志一致性的协议,相比于 Paxos 协议 Raft 更易于理解和去实现它。
8+
9+
## 上车
10+
11+
  Raft 通过选举一个领导人,然后给予他全部的管理复制日志的责任来实现一致性。领导人从客户端接收日志条目,把日志条目复制到其他服务器上,并且当保证安全性的时候告诉其他的服务器应用日志条目到他们的状态机中。拥有一个领导人大大简化了对复制日志的管理。例如:领导人可以决定新的日志条目需要放在日志中的什么位置而不需要和其他服务器商议,并且数据都从领导人流向其他服务器。一个领导人可以宕机,可以和其他服务器失去连接,这时一个新的领导人会被选举出来。
12+
13+
  Raft 把时间分割成任意长度的任期,任期用连续的整数标记。每一段任期从一次选举开始,一个或者多个候选人尝试成为领导者。如果一个候选人赢得选举,然后他就在接下来的任期内充当领导人的职责。在某些情况下,一次选举过程会造成选票的瓜分。在这种情况下,这一任期会以没有领导人结束;一个新的任期(和一次新的选举)会很快重新开始。Raft 保证了在一个给定的任期内,最多只有一个领导者。
14+
15+
  要实现 Raft 协议,参见下图:
16+
17+
![状态机持有的变量](../../img/state.png)
18+
19+
![投票请求](../../img/rpc_vote.png)
20+
21+
![日志发送](../../img/appent_rpc.png)
22+
23+
![服务器的应用规则](../../img/rule.png)
24+
25+
  Raft 协议将整个过程分为主要3个步骤:
26+
27+
1. 领导者:和其他一致性算法相比,Raft 使用一种更强的领导能力形式。比如,日志条目只从领导者发送给其他的服务器。这种方式简化了对复制日志的管理并且使得 Raft 算法更加易于理解。
28+
29+
2. 领导选举:Raft 算法使用一个随机计时器来选举领导者。这种方式只是在任何一致性算法都必须实现的心跳机制上增加了一点机制。在解决冲突的时候会更加简单快捷。
30+
31+
3. 关系调整:Raft 使用一种共同一致的方法来处理集群成员变换的问题,在这种方法中,两种不同的配置都要求的大多数机器会重叠。这就使得集群在成员变换的时候依然可以继续工作。
32+
33+
  后面将通过这3个主要过程进行展开。
34+
35+
## 发车(领导的选举)
36+
37+
  Raft 使用一种心跳机制来触发领导人选举。当服务器程序启动时,他们都是跟随者身份。如果一个跟随者在一段时间里没有接收到任何消息,也就是选举超时,然后他就会认为系统中没有可用的领导者然后开始进行选举以选出新的领导者。要开始一次选举过程,跟随者先要增加自己的当前任期号并且转换到候选人状态。然后他会并行的向集群中的其他服务器节点发送请求投票的 RPCs 来给自己投票。候选人的状态维持直到发生以下任何一个条件发生的时候,(a) 他自己赢得了这次的选举,(b) 其他的服务器成为领导者,(c) 一段时间之后没有任何一个获胜的人。当一个候选人从整个集群的大多数服务器节点获得了针对同一个任期号的选票,那么他就赢得了这次选举并成为领导人。每一个服务器最多会对一个任期号投出一张选票,按照先来先服务的原则。
38+
39+
  Raft 使用投票的方式来阻止候选人赢得选举除非这个候选人包含了所有已经提交的日志条目。候选人为了赢得选举必须联系集群中的大部分节点,这意味着每一个已经提交的日志条目肯定在这些服务器节点中至少存在一个上面。如果候选人的日志至少和大多数的服务器节点一样新,那么他一定持有了所有已经提交的日志条目。请求投票 RPC 实现了这样的限制: RPC 中包含了候选人的日志信息,然后投票人会拒绝掉那些日志没有自己新的投票请求。
40+
41+
  Raft 通过比较两份日志中最后一条日志条目的索引值和任期号定义谁的日志比较新。如果两份日志最后的条目的任期号不同,那么任期号大的日志更加新。如果两份日志最后的条目任期号相同,那么日志比较长的那个就更加新。
42+
43+
## 到站(日志复制)
44+
45+
  一旦一个领导人被选举出来,他就开始为客户端提供服务。客户端的每一个请求都包含一条被复制状态机执行的指令。领导人把这条指令作为一条新的日志条目附加到日志中去,然后并行的发起附加条目 RPCs 给其他的服务器,让他们复制这条日志条目。当这条日志条目被安全的复制,领导人会应用这条日志条目到它的状态机中然后把执行的结果返回给客户端。如果跟随者崩溃或者运行缓慢,再或者网络丢包,领导人会不断的重复尝试附加日志条目 RPCs(尽管已经回复了客户端)直到所有的跟随者都最终存储了所有的日志条目。
46+
47+
  Raft 的日志机制来维护一个不同服务器的日志之间的高层次的一致性。这么做不仅简化了系统的行为也使得更加可预计,同时他也是安全性保证的一个重要组件。Raft 维护着以下的特性,这些同时也组成了的日志匹配特性:
48+
49+
1. 如果在不同的日志中的两个条目拥有相同的索引和任期号,那么他们存储了相同的指令。
50+
2. 如果在不同的日志中的两个条目拥有相同的索引和任期号,那么他们之前的所有日志条目也全部相同。
51+
52+
  第一个特性来自这样的一个事实,领导人最多在一个任期里在指定的一个日志索引位置创建一条日志条目,同时日志条目在日志中的位置也从来不会改变。第二个特性由附加日志 RPC 的一个简单的一致性检查所保证。在发送附加日志 RPC 的时候,领导人会把新的日志条目紧接着之前的条目的索引位置和任期号包含在里面。如果跟随者在它的日志中找不到包含相同索引位置和任期号的条目,那么他就会拒绝接收新的日志条目。
53+
54+
  一致性检查就像一个归纳步骤:一开始空的日志状态肯定是满足日志匹配特性的,然后一致性检查保护了日志匹配特性当日志扩展的时候。因此,每当附加日志 RPC 返回成功时,领导人就知道跟随者的日志一定是和自己相同的了。
55+
56+
## 下车
57+
58+
源于 MIT,然后用于自己学习,[源码地址](https://github.com/happyer/distributed-computing)

src/raft/raft-impl.md

Lines changed: 0 additions & 72 deletions
This file was deleted.

0 commit comments

Comments
 (0)