以下是一些可供参考的讨论点:
这里以下列需求作为设计目标:
以下特性暂不支持:
除以上需求外,非功能性需求也要得到保证:
规模预估:
从单服务器开始,逐渐将系统拓展到支持百万级用户。单服务器设计如下:
简单地使用一台Apache web服务器和MySql数据库,以及一个本地路径 drive/ 作为根目录即可实现上面的系统。在 drive/ 目录下,每个用户对应一个文件夹,每个文件夹存储对应用户的文件,文件名与用户上传的原始文件名相同。以下是这个系统的示例:
主要需要3个API:上传文件,下载文件,获取文件历史 。
包含两种上传:
以下是一个断点重传的API示例:
https://api.example.com/files/upload?uploadType=resumable |
包含上传类型和本地文件数据两个参数。
一次断点重传的流程如下:
示例API: https://api.example.com/files/download
参数:
示例参数:
{ "path": "/recipes/soup/best_soup.txt" } |
示例API: https://api.example.com/files/list_revisions
参数:
以上所有的API都需要支持HTTPS,以保证数据是加密传输。
与其自建服务器分片与复制,不如将数据存储交给现有的云服务商来实现,这样可以借助云服务商成熟的服务和多数据中心来保障数据安全。以Amazon S3为例,Amazon S3支持同数据中心内的数据库复制和跨数据中心的数据库复制,通过将数据在多地进行冗余备份,可以有效保证数据安全及服务可用。
除了多地备份外,以下措施也必不可少:
经过以上措施的优化后,系统设计调整如下:
当两个用户同时修改了同一个文件时,冲突发生。这里我们以第一次修改提交为准,第二次提交被视为冲突,如下:
当用户2收到冲突通知时,服务器会一并返回服务器上最新的版本,由用户2自己来解决冲突。
以下是各组件介绍:
用户:包括客户端,浏览器,移动APP。
块服务器:用于将存储块上传到云存储。块级存储是指在云环境下将数据文件分块进行存储,每个块都包含一个唯一的哈希值,这个哈希值存储在元数据库中。对于云存储来说,每个块都是独立的,为了重建文件,需要将所有的块按顺序联合起来。块的大小可参考Dropbox,它的块大小是4MB。
云存储:存储数据块。
负载均衡:平摊请求到API服务器上。
API服务器:负责除了上传文件之外的全部请求,比如认证,用户配置,更新文件元数据等。
元数据库:存储用户、文件、存储块、版本信息等的元数据,不存储具体文件。
元数据缓存:缓存部分元数据,用于快速读取。
通知推送服务:用于推送事件通知,比如文件添加、删除、修改等事件。
离线备份队列:如果客户离线,无法拉取服务端文件的最新修改,那么离线备份队列会将这些修改存入队列,以便于在客户重新上线时推送文件修改。
深入探讨块服务器,元数据库,上传流程,下载流程,通知推送,节省存储空间,以及错误处理。
对于大文件,每次更新时都上传整个文件需要占用大量带宽,对此,可以从下面的两方面进行优化:
在当前系统中,块服务器承载了文件上传的全部压力。块服务器会把用户上传的文件分成合适的块,然后压缩并加密,块服务器还负责只上传那些被修改过的块。通过增量同步和数据压缩,可显著降低服务器的带宽占用。
本系统天然需要强一致性,因为同一时间用户看到的文件必须是相同的,系统要求在元数据缓存和数据库层保证强一致性。
缓存一般使用最终一致性模型,意味着不同副本可能不一样,为了实现强一致性,必须保证以下两点:
在关系型数据库中实现强一致性相对简单,因为关系型数据性支持ACID属性。但是非关系型数据库一般不支持ACID属性,需要由软件来保证在同步时能保持强一致性。在本系统设计中,我们使用关系型数据库,以支持ACID。
以下是一个简化的元数据库的表设计:
包含以下组件:
用户表:包含用户的基本信息,比如用户名,邮件地址,头像等。
设备表:存储设备信息,通过device_id来发送和接收推送消息。用户可能有多个设备。
工作空间:用户的根目录。
文件:存储文件的最新信息。
文件历史:存储文件的历史记录,现有行必须是只读的,以保证文件历史的完整性。
块:存储文件块的信息。任何版本的文件都可以通过块来组装。
这里有两个请求并行:上传元数据及上传文件到云存储,两个请求都从用户1中发出,用户2只用于接收推送消息。
当文件被新增或修改时,会触发下载流程。首先是一个客户端如何知道文件被修改另一个客户端修改的问题,这里有两种方式:
一旦用户知道文件已被修改,首先会通过API服务器向元数据库发起请求,然后下载相应的块以在本地重组文件,以下是下载流程:
为了保证强一致性,任何的修改都必须通知其他用户,以降低冲突风险,通知推送服务可以用于实现该目的。从高层次来说,推送服务可以在事件发生时将数据发送给用户,以下是一些可选项:
这里我们倾向于选择长轮徇,原因如下:
通知长轮徇,每个用户都和推送服务器维持一个长轮徇链接。当文件修改被客户端检测到时,客户端会首先关闭长轮徇链接,并下载最新的文件,然后重新建立长连接。
为支持文件历史版本以及保证可靠性,相同文件的多个历史版本会被保存在不同的数据中心,这会占用大量的存储空间。以下三点可以节约存储:
以下错误处理可供讨论:
略略略。