C|分布式|RPC&NFS

2021-11-22 11:27:26 浏览数 (1)

Intro

随着单机性能进入瓶颈,storage与serve的压力与日俱增,因此,这两个职责被分布在不同服务器上。由于原本单机的文件访问变为跨服务器,因此NFS(Network File System)诞生了。

大存储服务器负责文件系统,应用服务器负责响应客户端

但是,如果我不想进行原本代码的修改,而想让通过网络进行的文件访问看起来如同之前本地的访问一样呢?我们现在一般使用RPC(Remote Procedure Call)在原有的单机文件系统上进行一层封装,使之成为NFS.程序员所面对的编程接口依然和往常的接口相同,而变化的仅仅是底层实现。

RPC

允许进程在远端执行而无需编码交互细节

我们使用Stub中间件隐藏通信的交互细节,真正的RPC通过Stub进行,而用户代码毫无察觉。

Stub隐藏了通信的细节,使得上层的调用无需修改

Client stub

  • request中放置参数
  • send requset to server
  • 等待response

Service stub

  • 等待message
  • 获取request参数
  • 进程调用
  • response中放置结果与状态(success?/accecpted?)
  • send respones to client

问题在于,message中应该放置什么,以下是一些比较重要的信息

当前的call的标识-Transaction ID

调用什么方法–Service ID (e.g., function ID)

身份认证-Auth Stuff

使用什么参数–Service parameter (e.g., function parameter)

由于引用失效,参数的再编排–Using marshal / unmarshal

//跨地址空间引用失效,因此需要进行序列化/反序列化(及处理网络通信的大小端)

Components

为了搭建一个RPC框架,我们需要

1.RPC格式标准(UDP or TCP or HTTP2?)

2.marshal / unmarshal工具库

3.Stub Generator:产生Stub

Client:marshal arguments, call, wait, unmarshal reply

Server:unmarshal arguments, call real function, marshal reply

4.Framework:

Client:

正确分发message到对应的server stub

跟踪所有发出去的请求

将收到的响应匹配到对应的call

多个caller共用一个socket

请求超时、重传的处理

Server:

对每个thread/callback正确分发reply(每个请求分配一个线程,或者请求多时维护线程池)

5.Binding:Client如何找到对应的Server

6.网络传输(如socket)

网络通信导致的Trade-off

1.性能开销(但不是传文本可以不用HTTP,会快些)

2.超时造成的额外问题

一旦发生超时,有这么几种解决方案,一般RPC使用第一或者第二

At Least Once:

重复resend,直到收到响应(但是可能会收到一堆响应)->要求调用无副作用

At Most Once:

重复resend, Server只保留一个request而忽略重复进行处理 -> 要求幂等性,多次调用如一

Exactly Once:

难以实现(没学)

3.错误隔离(C/S崩溃不影响彼此)


NFS

eg: mount –t nfs 10.131.250.6:/nfs/dir /mnt/nfs/dir

在应用程序调用文件系统接口时,NFS的所有调用如下。

NFS顺序图

值得注意的几点

fd<-->fh (file handler)

和下面Server无状态有关,而fd在Client内存中。因此传递对Server有用的fh,内含:

  • file system ID
  • inode number(path name可能会被rename)
  • generation number-维护一致性(如果inode被删除后,又被复用number)

NFS Server并没有open/close

Server stateless,状态由Client维护(即使Server崩溃重启,由于无状态,依然能处理request)

回传file attributes

维护Client上的metadata

Cache

Server Reply Cache:

由于网络传输延迟或丢包,Client可能重复request,因此根据Transaction ID建立Cache。这样重复的request可以返回相同的响应。

Client Cache:

存储一些最近使用的vnode(virtual node),attributes,blocks。减少RPC延迟。

Cache coherence :

Read/write:

与单机不同,无法保证获取最新数据,自行负责解决

Close-to-open:

open时获取modified time,与cache中进行比较,更新到最新数据。

close时写回(类似于cache被淘汰时写回内存)

左图中:C2open时能获取最新的数据

右图中:C2open时,由于C1未close,因此open时没有更新,因此read脏数据。

由于上述情况,一般需要另外进行并发的处理,例如对文件加锁。

Vnode

虚拟化

把文件属于local还是remote抽象化,左侧的箭头可以指向NFS client,也可能指向Local file system,从而让程序员忽视了实现细节。


GFS(Google FS)

随着规模的增大,单文件服务器也无法承受了。为了scalable,GFS使用一个服务器作为转发,多个文件服务器进行数据传输。这里的核心架构在于,将控制流和数据流解耦。

Flat Namespace

尽管namespace看起来有树形结构,实际上并没有directory,而是把整个路径映射到chunk上(因为目录访问需要多次访问chunk,而网络传输的开销远高于硬盘)

GFS Cluster

Single Master

内存中维护metadata(没有inode,没有symlink,没有hard link)

使用前缀编码进行压缩(每个entry小于64bytes)

  • namespace
  • 访问控制
  • 映射表
  • chunk的位置

Multiple Chunkserver

存储数据(chunk64mb)

传送heartbeat信息(如果崩溃了,需要让master保持同步)

Cache

Client和Chunkserver没有数据的cache(因为chunk的容量很大),但是Client有chunkserver的cache,下次访问可以不通过master。

(由master进行lease,进行临时的权限移交)

Fault Tolerance

Chunkserver

在这里使用三备份,当三个备份都写入完成后再进行response。三个buffer一般不会同时崩溃,所以写入buffer后就可以视为写入完成。

对于每个chunk产生32bit的checksum

定期扫描罕用文件,检查一致性

Master

  • 对于所有metadata修改log
  • shadow master
  • 状态在多机器冗余存储

Typical Workload

搜索引擎特点

大的流读取 小的随机读取(因此适合这种大chunk)

大量顺序写操作(append),先前爬取的数据较少修改

多client同时append一个文件(因此没有什么对已有内容的修改,写方面不追求效率)

0 人点赞