实现一个大规模的文档存储服务
以前,我们解释了为什么我们决定为Genius Scan选择在文档存储服务上工作,以及我们是怎样逼近这个工程的挑战的。我们认为这个过程是值得分享得,我们也希望能够从社区获得反馈。
背景信息
在Grizzly实验室,我们创建了非常有帮助的生产率应用。我们的主要产品是Genius Scan,它是一个IOS和Android扫描器。它是最流行得移动应用之一,自从2010年6月诞生以来,网络上有超过1500万得下载量,我们从没有在广告上花一分钱,但是我们却经常收到大量的反馈。
Genius Scan是一个物理介质和数字世界之间的接口。它可以让你对一个纸质文档形成快照,纠正透视失真,并且可以将其处理的就像真的扫描仪一样。多做几次扫描之 后,你可以构建一个多页PDF文档。Genius Scan也非常擅长将数据导出到任何你想要的地方(Box, Dropbox, Email, Evernote, Expensify, Google Drive, OneNote, OneDrive, FTP, WebDAV…)
当我们期望用户使用Genius Scan扫描并导出文档时,我们注意到他们中的许多人使用Genius Scan长时间存储所扫描的数据。
要解决的用户问题
Genius Scan把文档存储在手机应用的本地文件夹里。由于应用是运行在沙盒里的,因此只有Genius Scan才可以访问这个文件夹,而且文档是无法在各种设备间实现同步的。除非你决定导出文件,否则这些文档是不会脱离手机的(对IOS自动备份例外)。这 么做有两个优点:实现简单,隐私性强。
然而, 经过4年对支持问题的邮件答复,我们可以清晰地看到用户所要求的模型:
-
备份。用户可能由于许多原因而丢失文档:无意间删除,不正确地安装新的OS更新,手机丢失或者被盗。假设用户使用了iCould,那么不太满意的解决方法就是自iCloud上完整地恢复手机备份。
-
设备间迁移(把文档从旧设备迁移到新设备)。跨平台迁移时就会出现问题,通常出现在iOS和安卓 平台之间。此时,用户没有一个明确的方法去访问他们在前一个平台上创建的文档。解决的方法是把文档导出到像Dropbox这样的云服务上。然后,用户就可 以通过新设备上安装的Dropbox应用来访问这些文档了。我们对这个安卓应用的备份和恢复进行了测试,得到的结果是它也不是一个完美的解决方法。在升级 iOS设备的时候如果用户没有进行设备备份安装,那么这样的问题也会出现。
-
同步(在另外的设备上访问这些文档)。用户用他们的手机对文档进行扫描,然后想在平板上阅读。目前,做到这一点的唯一方法就是把文档导出到像Dropbox这样的服务上,然后通过平板上安装的Dropbox应用来访问。采用同样的方法,用户可以在桌面电脑上查看这些文档。
-
其他功能:要求有其他处理能力或者后台服务。例如这样的功能:OCR,OCR索引(类似 Evernote 所做),文档签名和分享。
所有这些反馈都向我们呈现出这样的需求,即文档存储服务要具有同步功能。
自己添加的需求
我们要面对的主要挑战之一就是Genius Scan已经大规模的使用了。每月数百万使用用户每天大约扫描 250,000个新文档,而且已经扫描的文档要超过2亿个。我们能找到实现新产品的方法,不过我们不能只是为了以后的扩展而设计一个简单的架构。
看起来每周一都是忙着扫描的日子,不过没有看到有人扫描火鸡啊:)
我们的服务程序需要满足以下需求:
-
安全性和私密性:很显然,这个我们的服务程序首先要实现的功能。
-
数据完整性:用户把非常重要的文档存储在Genius Scan里(丢失要写入的数据会体现为数千美元的损失。)在用户启用同步的情况下不能有任何文档丢失。
-
处理网络变化的能力:移动设备长时间处于离线状态,仍可回到在线状态(例如:用户在乘飞机飞行期间扫描文档)。
-
跨平台:我们需要支持iOS,安卓和其他可能的平台。
-
功能可选:用户可以不需要启动同步,尤其在他们关心隐私的情况下。
-
资金上可持续:我们规划出能够长期运行这项服务的所有可能的费用。简单的电子表格和几项评估就足以说明费用的组成。在我们规划的费用表格里,每月的存储费用(S3)是最重要的。
其他选择
同步是非常常见的问题,可以选择许多其他方式来实现同步:
-
使用 iClould,iCloud 驱动,不要求在服务端做任何工作了。不过无法解决备份、设备间迁移问题,只是解决了同步问题。iCloud最让我们揪心的问题是它不稳定,不过在iOS7和8上这个问题解决了。
-
CloudKit 也是一个可选的解决方案。不过,考虑到我们的存储容量,这个方案不是很适合,因为它有存储容量限制。除此之外,这个方案还仅限于iOS。
-
跨平台的云存储服务:Dropbox,Box,OneDrive...我们可以把这些服务当做后台服 务,来解决备份、设备间迁移和同步问题,这样我们就不需要在制作一套这样的东西了。不过存在几个缺陷:我们需要适应它们的同步模式-在Dropbox里, 同步的单元是文件-另外,我们还依赖第三方平台。最后,更难提供任何新增的功能了(索引、搜索,OCR),因为我们无法管理文档存储。
-
Ensembles: 由Drew McComack开发的Ensembles给人印象深刻,是一个很适合的解决方案。不过,它只同步存储在核心数据里的对象:实现对扫描文件自身的同步就很 困难。另外,它还不支持安卓平台(虽然这么说,不过Ensemebles 1.x是开源项目,可以进行扩展)。这个云服务是用来做临时传输的(采用的是p2p同步模式),不适合于数据存储,因此不能用来备份和添加新功能。
-
我们自己的服务:它将解决各种不同的功能需求,不过主要的缺点是我们需要重新编写一套软件。
通过与旧金山和法国的几个顶级公司接触之后,再加上其他的解决方案都存在缺陷,因此我们决定着手实现我们自己的服务。
开发方式
早早地确认问题所在对我们来说非常重要。不过,持续不断地列出可能出现的问题和极端情形,延缓实现则更容易些,因此我们决定采用迭代的方法实现我们的目标。
指标
我们正在通过测量和调查两个渠道对我们每个月数百万的活跃用户(MAU)在服务上所产生的负荷进行评估。可以对许多指标进行测量。至于调查,我们可以在Genius Scan里显示内置的广告条来询问用户。我们还可以请求用户联系我们以获得支持。
一个重要的测量指标是普通用户(以及超级用户)通常存储多少文档。我们还希望评估每天扫描文档的最大数量。
除此之外,对用户的调查还可以让我们理解资金是否可持续:用户是否已经准备为这个服务付费了?我们是不是应该给每个用户提供功能受限的测试版呢?等等。
在安卓应用中使用Dropbox SDK
起初,我们想将某种同步功能与各版本的Genius Scan整合到一块。我们首先实现了一个简单的原型,通过使用 Dropbox Sync API修改了安卓版的Genius Scan。这项工作相当快,几天时间就做了一个粗略的实现。
这样的原型有几个好处:第一,关于项目可行性方面它给了我们自信。第二,它让我们有一些具体东西可以考虑到客户端应用的用户体验;最后,识别安卓上的潜在问题通常是第一步(比如典型得同步冲突是什么,我们所期望的用户行为是什么)。
自定义实现
下一步我们将开发我们自己的C/S解决方案。这一次,我们专注于IOS应用。我们使用Sinatra开发了一个简单的后台。我们服务器端的实现包含了广泛使用的IOS应用specs和Ruby。
我们得架构是基于Evernote同步。同步的核心在客户端,服务器存储状态。在某些方面来说,模型结构比较类似:在 Genius Scan中,文档包含页面,可以选择性标记。在Evernote中,笔记本包含笔记,也可以选择性标记。
我们客户端实现很好地适配了Genius Scan 以前编写的核心数据代码。另外,令人吃惊的是代码不是嵌入的:所有的同步是在各自不同的管理对象里进行的,而且管理对象间是通过分离的通知来进行通信的。
这个iOS原型使我对同步的理解更为深刻。此实现的第一次迭代花费了不到一周的时间进行开发,期间并没有出现任何冲突。
我们正在对早期的实现进行改善(有关这方面的内容,我们计划写一篇更加注重技术方面的博客文章。)。注意,这次改善仍然大量的集中在客户端,而且服 务器部分是运行在本地的高速网络上的。接下来的一个行动就是在实际的网络环境下对这次改善实现的测试,其中包括网络延迟和网络出错。
结论
我们两个工作在这个项目上是超级兴奋的,我们已经推迟了一段时间。我们想尽可能的快来给我们的用户提供服务。但是,我们不着急。因为我们完全没有压力,我们可以花时间将它做好。
如果你读到这了,我们对您的反馈(Hacker News)也很感兴趣。无论如何放心好了,我们会及时通知您我们的进展的。