Files
MIT6.824/lecture-03-gfs/3.6-xie-wen-jian-write-file.md
2022-01-25 02:41:31 +00:00

11 KiB
Raw Blame History

3.6 GFS写文件Write File1

GFS写文件的过程会更加复杂且有趣。从应用程序的角度来看写文件和读文件的接口是非常类似的它们都是调用GFS的库。写文件是应用程序会告诉库函数说我想对这个文件名的文件在这个数据段写入当前存在buffer中的数据。让我Robert教授稍微收敛一下我只想讨论数据的追加。所以我会限制这里的客户端接口客户端也就是应用程序只能说我想把buffer中的数据追加到这个文件名对应的文件中。这就是GFS论文中讨论的记录追加Record Append。所以再次描述一下对于写文件客户端会向Master节点发送请求说我想向这个文件名对应的文件追加数据请告诉我文件中最后一个Chunk的位置。

当有多个客户端同时写同一个文件时一个客户端并不能知道文件究竟有多长。因为如果只有一个客户端在写文件客户端自己可以记录文件长度而多个客户端时一个客户端没法知道其他客户端写了多少。例如不同客户端写同一份日志文件没有一个客户端会知道文件究竟有多长因此也就不知道该往什么样的偏移量或者说向哪个Chunk去追加数据。这个时候客户端可以向Master节点查询哪个Chunk服务器保存了文件的最后一个Chunk。

对于读文件来说可以从任何最新的Chunk副本读取数据但是对于写文件来说必须要通过Chunk的主副本Primary Chunk来写入。对于某个特定的Chunk来说在某一个时间点Master不一定指定了Chunk的主副本。所以写文件的时候需要考虑Chunk的主副本不存在的情况。

对于Master节点来说如果发现Chunk的主副本不存在Master会找出所有存有Chunk最新副本的Chunk服务器。如果你的一个系统已经运行了很长时间那么有可能某一个Chunk服务器保存的Chunk副本是旧的比如说还是昨天或者上周的。导致这个现象的原因可能是服务器因为宕机而没有收到任何的更新。所以Master节点需要能够在Chunk的多个副本中识别出哪些副本是新的哪些是旧的。所以第一步是找出新的Chunk副本。这一切都是在Master节点发生因为现在是客户端告诉Master节点说要追加某个文件Master节点需要告诉客户端向哪个Chunk服务器也就是Primary Chunk所在的服务器去做追加操作。所以Master节点的部分工作就是弄清楚在追加文件时客户端应该与哪个Chunk服务器通信。

每个Chunk可能同时有多个副本最新的副本是指副本中保存的版本号与Master中记录的Chunk的版本号一致。Chunk副本中的版本号是由Master节点下发的所以Master节点知道对于一个特定的Chunk哪个版本号是最新的。这就是为什么Chunk的版本号在Master节点上需要保存在磁盘这种非易失的存储中的原因见3.4因为如果版本号在故障重启中丢失且部分Chunk服务器持有旧的Chunk副本这时Master是没有办法区分哪个Chunk服务器的数据是旧的哪个Chunk服务器的数据是最新的。

学生提问为什么不将所有Chunk服务器上保存的最大版本号作为Chunk的最新版本号

Robert教授当Master重启时无论如何都需要与所有的Chunk服务器进行通信因为Master需要确定哪个Chunk服务器存了哪个Chunk。你可能会想到Master可以将所有Chunk服务器上的Chunk版本号汇总找出里面的最大值作为最新的版本号。如果所有持有Chunk的服务器都响应了那么这种方法是没有问题的。但是存在一种风险当Master节点重启时可能部分Chunk服务器离线或者失联或者自己也在重启从而不能响应Master节点的请求。所以Master节点可能只能获取到持有旧副本的Chunk服务器的响应而持有最新副本的Chunk服务器还没有完成重启或者还是离线状态这个时候Master能找到的Chunk最大版本明显不对

当Master找不到持有最新Chunk的服务器时该怎么办Master节点会定期与Chunk服务器交互来查询它们持有什么样版本的Chunk。假设Master保存的Chunk版本是17但是又没有找到存储了版本号是17的Chunk服务器那么有两种可能要么Master会等待并不响应客户端的请求要么会返回给客户端说我现在还不知道Chunk在哪过会再重试吧。比如说机房电源故障了所有的服务器都崩溃了我们正在缓慢的重启。Master节点和一些Chunk服务器可能可以先启动起来一些Chunk服务器可能要5分钟以后才能重启这种场景下我们需要等待甚至可能是永远等待因为你不会想使用Chunk的旧数据。

所以总的来说在重启时因为Master从磁盘存储的数据知道Chunk对应的最新版本Master节点会整合具有最新版本Chunk的服务器。每个Chunk服务器会记住本地存储Chunk对应的版本号当Chunk服务器向Master汇报时就可以说我有这个Chunk的这个版本。而Master节点就可以忽略哪些版本号与已知版本不匹配的Chunk服务器。

回到之前的话题当客户端想要对文件进行追加但是又不知道文件尾的Chunk对应的Primary在哪时Master会等所有存储了最新Chunk版本的服务器集合完成然后挑选一个作为Primary其他的作为Secondary。

之后Master会增加版本号并将版本号写入磁盘这样就算故障了也不会丢失这个数据。

接下来Master节点会向Primary和Secondary副本对应的服务器发送消息并告诉它们谁是Primary谁是SecondaryChunk的新版本是什么。Primary和Secondary服务器都会将版本号存储在本地的磁盘中。这样当它们因为电源故障或者其他原因重启时它们可以向Master报告本地保存的Chunk的实际版本号。

学生提问如果Chunk服务器上报的版本号高于Master存储的版本号会怎么样

Robert教授这个问题很好。我不知道答案不过论文中有一些线索。其实刚刚我的介绍有一些错误我认为你的问题让我明白了一些事情。GFS论文说如果Master节点重启并且与Chunk服务器交互同时一个Chunk服务器重启并上报了一个比Master记住的版本更高的版本。Master会认为它在分配新的Primary服务器时出现了错误并且会使用这个更高的版本号来作为Chunk的最新版本号。

当Master向Primary和Secondary发送完消息之后就崩溃了可能会出现上面这种情况。为了让Master能够处理这种情况Master在发送完消息之后需要将Chunk的最新版本写入到磁盘中。这里的写入或许需要等到Primary和Secondary返回确认消息之后。

学生提问:听不清(但是应该与这一节的第一个问题一样)。

Robert教授我不认为这行得通。因为存在这种可能性当Master节点重启时存储了Chunk最新版本号的Chunk服务器是离线状态。这种情况下我们不希望Master在重启时不知道当前的版本号因为那样的话Master就会认为当前发现的最高版本号是当前版本号但是由于有最新版本号的Chunk服务器还是离线状态发现的最高版本号可能是个旧版本号。

Robert教授之前没太关注这块所以我也不太确定Master究竟是先写本地磁盘中的版本号然后再通知Primary和Secondary还是反过来。但是不管怎么样Master会更新自己的版本号并通知Primary和Secondary说你们现在是Primary和Secondary并且版本号更新了。

所以现在我们有了一个Primary它可以接收来自客户端的写请求并将写请求应用在多个Chunk服务器中。之所以要管理Chunk的版本号是因为这样Master可以将实际更新Chunk的能力转移给Primary服务器。并且在将版本号更新到Primary和Secondary服务器之后如果Master节点故障重启还是可以在相同的Primary和Secondary服务器上继续更新Chunk。

现在Master节点通知Primary和Secondary服务器你们可以修改这个Chunk。它还给Primary一个租约这个租约告诉Primary说在接下来的60秒中你将是Primary60秒之后你必须停止成为Primary。这种机制可以确保我们不会同时有两个Primary我们之后会再做讨论3.7的问答中有一个专门的问题讨论)。

我们现在来看GFS论文的图2。假设现在Master节点告诉客户端谁是Primary谁是SecondaryGFS提出了一种聪明的方法来实现写请求的执行序列。客户端会将要追加的数据发送给Primary和Secondary服务器这些服务器会将数据写入到一个临时位置。所以最开始这些数据不会追加到文件中。当所有的服务器都返回确认消息说已经有了要追加的数据客户端会向Primary服务器发送一条消息说你和所有的Secondary服务器都有了要追加的数据现在我想将这个数据追加到这个文件中。Primary服务器或许会从大量客户端收到大量的并发请求Primary服务器会以某种顺序一次只执行一个请求。对于每个客户端的追加数据请求也就是写请求Primary会查看当前文件结尾的Chunk并确保Chunk中有足够的剩余空间然后将客户端要追加的数据写入Chunk的末尾。并且Primary会通知所有的Secondary服务器也将客户端要追加的数据写入在它们自己存储的Chunk末尾。这样包括Primary在内的所有副本都会收到通知将数据追加在Chunk的末尾。

但是对于Secondary服务器来说它们可能可以执行成功也可能会执行失败比如说磁盘空间不足比如说故障了比如说Primary发出的消息网络丢包了。如果Secondary实际真的将数据写入到了本地磁盘存储的Chunk中它会回复“yes”给Primary。如果所有的Secondary服务器都成功将数据写入并将“yes”回复给了Primary并且Primary也收到了这些回复。Primary会向客户端返回写入成功。如果至少一个Secondary服务器没有回复Primary或者回复了但是内容却是抱歉一些不好的事情发生了比如说磁盘空间不够或者磁盘故障了Primary会向客户端返回写入失败。

GFS论文说如果客户端从Primary得到写入失败那么客户端应该重新发起整个追加过程。客户端首先会重新与Master交互找到文件末尾的Chunk之后客户端需要重新发起对于Primary和Secondary的数据追加操作。