2.2 System structure
Chubby有两个通过RPC进行通信的主要组件:一个是服务器,另一个是客户端应用程序连接的库;见图1。Chubby客户端和服务器之间的所有通信都是由客户端库介导的。第3.1节中讨论了可选的第三个组件,即代理服务器。
一个Chubby单元由一小群服务器(通常是5个)组成,称为副本,放置在一起以减少相关故障的可能性(例如,在不同的机架上)。副本使用分布式共识协议来选举领导者;领导者必须获得大多数副本的投票,并承诺这些副本在几秒钟的间隔内不会选举不同的领导者,这被称为主控租赁(master lease)。只要领导者继续赢得多数票,领导者的租约就会被副本定期更新。
副本维护一个简单的数据库的备份,但只有领导者能发起对该数据库的读写。所有其他的副本只是复制来自领导者的更新,使用共识协议发送。
客户端通过向DNS中列出的副本发送主站位置请求来找领导者。非主副本对这种请求的回应是返回领导者的身份。一旦客户端找到了领导者,客户端就会将所有的请求指向它,直到它不再响应,或者它表明它不再是领导者。写入请求通过共识协议传播到所有的副本;当写入请求到达单元中的大多数副本时,这些请求被确认。读取请求仅由领导者满足;只要领导者租约没有过期,这就是安全的,因为不可能存在其他领导者。如果一个领导者失败了,其他副本在其领导者租约到期时运行选举协议;通常在几秒钟内就能选出一个新的领导者。例如,最近的两次选举花了6秒和4秒,但我们也看到高达30秒的数值(§4.1)。
如果一个副本发生故障,并且在几个小时内没有恢复,一个简单的替换系统会从一个空闲的池中选择一个新的机器,并在其上启动锁服务器程序。然后,它更新DNS表,用新副本的IP地址替换故障副本的IP地址。当前的领导者定期轮询DNS,并最终注意到这一变化。然后领导者更新单元数据库中的单元成员列表;这个列表通过正常的复制协议在所有成员中保持一致。同时,新的副本从存储在文件服务器上的备份和活动副本的更新中获得数据库的最新副本。一旦新副本处理了当前领导者等待提交的请求,该副本就被允许在新领导者的选举中投票。
2.3 Files, directories, and handles
Chubby输出了一个类似于UNIX[22]的文件系统界面,但比UNIX的更简单。它由一个严格的文件目录树组成,样式很常见,名称部分由斜线分开。一个典型的名字是:
/ls/foo/wombat/pouch
ls前缀是所有Chubby名字的共同点,代表锁服务。第二个组成部分(foo)是Chubby单元的名称;它通过DNS查询被解析到一个或多个Chubby服务器。一个特殊的单元名称local表示应该使用客户的本地Chubby单元;这通常是在同一建筑物内的单元,因此最可能被访问。名称的其余部分,/wombat/pouch,在命名的Chubby单元内解释。再次遵循UNIX,每个目录包含一个子文件和目录的列表,而每个文件包含一串未解释的字节。
由于Chubby的命名结构类似于一个文件系统,我们能够通过它自己的专门的API提供服务,也可以通过我们其他文件系统(如谷歌文件系统)使用接口。这大大减少了编写基本浏览和名称空间操作工具所需工作量,并减少了对Chubby普通用户的上手难度。
该设计与UNIX的不同之处在于便于分发。为了使不同目录下的文件能够从不同的Chubby主服务器得到服务,我们没有暴露可以将文件从一个目录移动到另一个目录的操作,我们没有维护目录的修改时间,并且我们避免了路径依赖的权限语义(也就是说,对文件的访问是由文件本身的权限控制的,而不是由通往文件的路径上的目录控制的)。为了便于缓存文件元数据,系统不显示最后访问时间。
命名空间只包含文件和目录,统称为节点。每个节点在其单元中只有一个名字。没有符号或硬链接。
节点可以是永久性的,也可以是短暂性的。任何节点都需要被显式删除,但如果没有客户端打开它们,短暂的节点等价于被删除的节点(比如目录,它们是不存在的)。短暂的文件被用作临时文件,并作为对其他用户的指标,表明一个客户端在线。任何节点都可以作为一个建议性的读/写锁;这些锁在第2.4节有更详细的描述。
每个节点都有各种元数据,包括三个访问控制列表(ACL)的名称,用于控制节点的读、写和变更ACL名称。除非被重写,否则一个节点在创建时继承其父目录的ACL名称。ACL本身就是位于ACL目录下的文件,该目录是单元格本地命名空间的一个已知部分。这些ACL文件由简单的负责人姓名列表组成;读者可能会想起Plan 9的组[21]。因此,如果文件F的写入ACL名称是foo,而ACL目录中的文件foo包含一个条目bar,那么用户bar就被允许写入F。用户通过RPC系统内置的机制进行认证。因为Chubby的ACL是简单的文件,所以它们可以自动提供给希望使用类似访问控制机制的其他服务。
每个节点的元数据包括四个单调递增的64位数字,允许客户端轻松检测变化。
- 实例号;大于之前任何具有相同名称的节点的实例号。
- 内容生成号(仅文件);当文件的内容被写入时,这个数字会增加。
- 一个锁的生成号码;当节点的锁从free过渡到hold时,这个号码会增加。
- ACL生成数;当节点的ACL名称被写入时,这个数字会增加。
Chubby还开放了一个64位的文件内容校验,因此客户可以判断文件是否不同。
客户端打开节点以获得类似于UNIX文件描述符的句柄。句柄包括:
- 校验码,防止客户端创建或猜测句柄,因此只有在创建句柄时才需要进行全面的访问控制检查(与UNIX相比,UNIX在打开时检查其权限位,但不是在每次读/写时,因为文件描述符不能被伪造)。
- 一个序列号,它允许领导者程序知道一个句柄是由它还是由以前的领导者程序产生的。
- 在打开时提供的模式信息,如果一个旧的句柄被提交给一个新重启的主领导者,允许主领导者重新创建其状态。