近年来,云存储服务如谷歌Drive、Dropbox、Microsoft OneDrive、苹果的iCloud已经变得非常流行。在本章中,你需要设计谷歌开车。
在进入设计之前,让我们花点时间了解一下谷歌驱动。谷歌Drive是一个文件存储和同步服务,可以帮助你存储文档、照片、视频和云中的其他文件。你可以从任何一台电脑上访问你的文件,智能手机和平板电脑。您可以轻松地与朋友、家人和同事共享这些文件[1]。图15-1和图15-2展示了谷歌驱动器在浏览器和手机上的样子分别应用程序。
设计一个谷歌驱动器是一个大项目,因此提出问题以缩小范围是很重要的的范围。
候选人:最重要的特征是什么?
面试官:上传和下载文件,文件同步和通知。
候选人:这是一个移动应用程序,还是一个web应用程序,还是两者兼有?
面试官:这两个。
候选人:支持的文件格式是什么?
面试官:任何文件类型。
候选人:文件需要加密吗?
面试官:是的,存储中的文件必须加密。
候选人:是否有文件大小限制?
面试官:可以,文件必须小于等于10gb。
候选人:这个产品有多少用户?
面试官:1000万DAU。
本章主要关注以下几个特性。
本章不讨论的特性包括:
后面的包络估计
我们不从一开始就显示高级设计图,而是使用一个稍微不同的方法。我们将从简单的事情开始:在单个服务器中构建所有内容。然后,逐渐扩展到支持数百万用户。通过这个练习,它会刷新你对书中一些重要主题的记忆。
让我们从单个服务器设置开始,如下所示:
我们花了几个小时设置了一个Apache web服务器,一个MySql数据库和一个目录名为drive/的根目录,用于存放上传的文件。在驱动器/目录下,有一个列表目录的名称,称为命名空间。每个命名空间都包含上传的所有文件用户。服务器上的文件名与原始文件名保持一致。每个文件或文件夹可以通过连接命名空间和相对路径来唯一标识。
图15-3展示了左侧的/drive目录及其目录的样子右侧展开视图。
api
api是什么样的?我们主要需要3个api:上传文件,下载文件和获取文件修订。
下面是一个resume upload API的例子:
https://api.example.com/files/upload?uploadType=resumable
Params:
可恢复上传通过以下3个步骤实现[2]:
- 发送初始请求,获取可恢复的URL。
- 上传数据并监控上传状态。
- 如果上传受到干扰,请恢复上传。
你花了一整夜的时间来设置数据库分片并密切监控它。一切工作又顺利。您已经停止了火灾,但您仍然担心潜在的数据存储服务器中断造成的损失。你四处打听,还有你的后台专家朋友弗兰克我说过,Netflix和Airbnb等许多领先公司都使用Amazon S3进行存储。Amazon S3 (Amazon Simple Storage Service)是一种提供对象存储服务的服务业界领先的可扩展性、数据可用性、安全性和性能。你决定
做一些调查,看看它是否合适。
在大量阅读之后,您可以很好地理解S3存储系统并决定使用在S3中存储文件。Amazon S3支持同区域和跨区域复制。一个区域是亚马逊网络服务(AWS)数据中心所在的地理区域。如图所示15-6、数据可以复制在同一区域(左边)和跨区域(右边)。冗余文件存储在多个区域,以防止数据丢失和保证可用性。存储桶类似于文件系统中的文件夹。
将文件放入S3后,您终于可以放心地睡个好觉了数据损失。为了防止类似的问题在将来再次发生,你决定做更多的事情研究一下你可以改进的地方。以下是你会发现的几个方面:
应用上述改进后,您已经成功解耦了web服务器,元数据数据库,以及来自单个服务器的文件存储。更新后的设计显示在图15-7。
同步冲突
对于像谷歌驱动器这样的大型存储系统,同步冲突时有发生。当两个用户同时修改同一个文件或文件夹,产生冲突。我们怎么能解决冲突?这是我们的策略:第一个被处理的版本获胜,而稍后处理的版本会收到冲突。图15-8展示了一个同步的例子冲突。
在图15-8中,用户1和用户2试图同时更新同一个文件,但更新的是用户1的系统首先对文件进行处理。用户1的更新操作通过了,但是,用户2得到了同步冲突。我们如何解决用户2的冲突?我们的系统显示两个副本用户2的本地副本和服务器的最新版本(图15-9)。用户2有合并两个文件或覆盖一个版本与另一个的选项。
当多个用户同时编辑同一文档时,保持文件同步。有兴趣的读者可以参考参考资料[4][5]。
高级设计
图15-10展示了提议的高层设计。让我们检查的每个组成部分这个系统。
用户:用户通过浏览器或移动应用程序使用应用程序。
块服务器:块服务器将数据块上传到云存储。块存储,简称为块级存储是一种在云环境下存储数据文件的技术。一个文件可以拆分为多个块,每个块具有唯一的哈希值,存储在元数据中数据库。每个块被视为一个独立的对象并存储在我们的存储系统中(S3)。为了重建一个文件,块按特定的顺序连接。至于块大小,我们使用Dropbox作为参考:它将一个块的最大大小设置为4MB[6]。
云存储:文件被分割成更小的块存储在云存储中。
冷库:冷库是一种用于存储不活跃数据的计算机系统长时间无法访问文件。
负载均衡器:负载均衡器将请求均匀地分发到API服务器。
API服务器:它们负责除了上传流程之外的几乎所有事情。API服务器用于用户身份验证、管理用户配置文件、更新文件元数据等。
元数据数据库:存储用户、文件、块、版本等的元数据。请注意文件存储在云端,元数据数据库只包含元数据。
元数据缓存:缓存一些元数据以快速检索。
通知服务:它是一个允许数据传输的发布者/订阅者系统当某些事件发生时,从通知服务到客户端。在我们的具体例子中,通知当在其他地方添加/编辑/删除文件时,服务通知相关客户端,以便他们可以拉取最新的更改。
离线备份队列:如果客户端处于离线状态,无法拉取最新的文件更改,则离线备份队列存储信息,以便在客户端在线时同步更改。从高层讨论了谷歌驱动的设计。有些组件是复杂的,值得仔细检查的;我们将在深入讨论中详细讨论这些。
在本节中,我们将仔细研究以下内容:块服务器、元数据数据库、上传流程,下载流程,通知服务,节省存储空间和故障处理。
块服务器
对于定期更新的大文件,在每次更新时发送整个文件需要消耗a大量的带宽。提出了两种优化方法来最小化网络流量传播:
在我们的系统中,块服务器完成了上传文件的繁重工作。块服务器处理来自客户端的文件,方法是将文件分割成块,压缩每个块,然后加密。不需要上传整个文件到存储系统,只需要修改即可传输块。
图15-11展示了添加新文件时块服务器的工作方式。
图15-12展示了增量同步,这意味着只有被修改的块才会被传输到云端存储。高亮显示的区块“区块2”和“区块5”代表更改的区块。使用δ同步时,只有这两个块被上传到云存储。
块服务器通过提供增量同步和压缩来节省网络流量。
高一致性要求
我们的系统默认要求强一致性。文件被展示是不可接受的由不同的客户同时提供不同的服务。系统需要提供强有力的支持元数据缓存和数据库层的一致性。
内存缓存默认采用最终一致性模型,这意味着不同副本可能具有不同的数据。要实现强一致性,就必须保证后:
在关系数据库中实现强一致性很容易,因为它维护了ACID(原子性、一致性、隔离性、持久性)属性然而,NoSQL数据库可以默认不支持ACID属性。ACID属性必须以编程方式实现合并到同步逻辑中。在我们的设计中,我们选择关系型数据库是因为酸是原生支持的。
元数据数据库
如图15-13所示。请注意,这是高度简化的版本,因为它只包含最重要的表和有趣的字段。
User: User表包含用户的基本信息,如用户名、email、头像照片等。
Device: Device table用于存储设备信息。Push_id用于发送和接收mobile推送通知。请注意,一个用户可以有多个设备。
命名空间:命名空间是用户的根目录。
文件:文件表存储与最新文件相关的所有内容。
File_version:存储文件的版本历史。现有的行是只读的文件修订历史的完整性。
块(Block):存储与文件块相关的所有内容。任何版本的文件都可以被重建
通过按正确的顺序连接所有块。
上传流量
我们来讨论一下客户端上传文件时会发生什么。为了更好地理解流程,我们请绘制如图15-14所示的时序图。
在图15-14中,并行发送两个请求:添加文件元数据和上传文件到云存储。两个请求都来自客户端1。
添加文件元数据。
一旦客户端知道文件被修改,它首先通过API服务器请求元数据,然后下载块来构造文件。具体流程如图15-15所示。注意,只有由于空间限制,大多数重要的组件都显示在图中。
通知服务
为了保持文件的一致性,任何在本地执行的文件修改都需要通知其他客户减少冲突。通知服务就是为这个目的而构建的。在高层次的通知服务允许在事件发生时将数据传输到客户端。在这里有一些选择:
尽管这两种方式都能很好地工作,但我们选择长轮询的原因如下。
使用长轮询时,每个客户端都建立到通知服务的长轮询连接。如果当检测到对文件的更改时,客户端将关闭长轮询连接。关闭连接意味着客户端必须连接到元数据服务器才能下载最新的元数据的变化。在收到响应或到达连接超时后,立即启动客户端发送一个新请求保持连接打开。
节省存储空间
为了支持文件的版本历史记录,保证可靠性,同一个文件可以有多个版本跨多个数据中心存储。存储空间会因频繁使用而迅速被填满所有文件修订的备份。提出了三种降低存储成本的技术:
将不常用的数据移到冷库。冷数据是指没有被处理过的数据活跃数月或数年。像亚马逊S3冰川[11]这样的冷库比S3。
故障处理
故障可能发生在大型系统中,我们必须采用设计策略来解决这些失败。你的面试官可能会对你如何处理以下系统故障:
在本章中,我们提出了一种支持谷歌驱动的系统设计。结合强一致性、低网络带宽和快速同步使设计变得有趣。我们的设计包含两个流程:管理文件元数据和文件同步。通知服务是另一个系统的重要组成部分。它使用长轮询让客户端了解最新的文件的变化。
就像任何系统设计面试问题一样,没有完美的解决方案。每个公司都有它的独特约束,你必须设计一个系统来适应这些约束。知道了权衡你的设计和技术选择很重要。如果还有几分钟,你可以谈论不同的设计选择。
例如,我们可以直接从客户端将文件上传到云存储中,而不是去通过块服务器。这种方法的优点是它使文件上传更快因为一个文件只需要传输一次到云存储。在我们的设计中,文件是先转移到块服务器,再转移到云存储。然而,新方法有一些缺点:
系统的另一个有趣演变是将在线/离线逻辑移动到单独的服务。我们把它称为在线服务。通过将到场服务移出通知服务器,在线/离线功能可以很容易地与其他服务集成。
恭喜你走到了这一步!现在给自己一点鼓励吧。好工作!
Reference materials
[1] Google Drive: https://www.google.com/drive/
[2] Upload file data: https://developers.google.com/drive/api/v2/manage-uploads
[3] Amazon S3: https://aws.amazon.com/s3
[4] Differential Synchronization https://neil.fraser.name/writing/sync/
[5] Differential Synchronization youtube talk https://www.youtube.com/watch?
v=S2Hp_1jqpY8
[6] How We’ve Scaled Dropbox: https://youtu.be/PE4gwstWhmc
[7] Tridgell, A., & Mackerras, P. (1996). The rsync algorithm.
[8] Librsync. (n.d.). Retrieved April 18, 2015, from https://github.com/librsync/librsync
[9] ACID: https://en.wikipedia.org/wiki/ACID
[10] Dropbox security white paper:
https://www.dropbox.com/static/business/resources/Security_Whitepaper.pdf
[11] Amazon S3 Glacier: https://aws.amazon.com/glacier/faqs/