雷锋网按:本文由数据科学家董飞翻译自Erik Duindam发表在Medium上的文章《How I built an app with 500,000 users in 5 days on a $100 server》,公号:董老师在硅谷。
最近Pokémon Go风靡全球,让苹果和任天堂也坐地赚翻,很多开发者也坐不住了,绞尽脑汁想怎么做相关开发也跟上潮流。这一篇就是一个老司机讲述在5天获取50万Pokémon GO粉丝的APP是怎么做出来的?(跑在100刀的服务器上),谈到MVP(minimum viable product,最简可行产品)、敏捷开发、流行框架、技术选型、规模瓶颈,笔者觉得总体实战还是蛮强的,就翻译了推荐给大家。
现在创业公司都是要做MVP而不去考虑太多技术的扩展性。有种说法,强调要先把产品做出来,只要到时业务规模能顶得住,就不该浪费时间和精力在技术上。而你要做的是测试假设,验证市场和获取关注,以后才考虑规模化。但这种盲目导致了一些可怕的失败,下面这个故事会提醒大家。
最近Jonathan Zarra做了个基于Pokémon GO的APP叫GoChat,他在5天就做出了Pokémon GO聊天工具,100万的用户。他马上跑去见VC谈融资和增长故事。但紧接着GoChat就挂了。很多用户流失,而他们花的钱也追不回,这简直是个耻辱。
有个报道说Zarra在100万用户的时候度过了艰难时光。他从没想过这么多用户。他通过MVP理念搭建APP,后来才考虑规模化。他当时找了合同工去补救一些性能问题。合同工提到服务器成本是4000刀,我假想硬件是4000刀,包括虚拟机和网络流量成本。
我之前设计并搭建了上亿用户的Web平台。我觉得对于一个聊天App来说4000刀根本没必要,就算对于MVP来说。这只能说这个App的服务端技术很烂。搭建一个高效,可扩展的系统对于月活百万来说不容易,但也不是说做一个建立在云端便宜服务器上服务相当多用户的系统有多难。关键是在搭建MVP的时候多想想,做正确的选择。
跟GoChat类似,我上周也开发了一个Pokémon Go粉丝App叫GoSnaps。可以分享Pokémon Go截屏和地图。可以理解成Pokémon Go里面的Instagram/Snapchat。GoSnaps第一天就涨了6万粉,第二天16万,五天后就是50万独立用户。现在上传了15万到20万的snaps。任何时刻都是上千并发用户,我也搭建了图像识别的软件去自动检测上传图片是不是Pokémon GO相关,以及图片的大小调整工具。我用Google的云服务,一个月100刀,加上便宜的Google云存储来保存图片,现在看起来性能还不错。
我们可以比较GoChat和GoSnaps。两个APP都是每秒生成很多请求去聊天或者拿到地图某区域的图片。这里有数据库或者基于搜索引擎的GEO查询,要么选经纬度的多边形或者是特定点。我们使用多边形并在某人移动地图时去发出请求。这些类型的查询是数据库中很重的操作,特别是排序和过滤的组合。我们每秒都有上百个这样类型的请求,GoChat应该也差不多。
GoChat不同的是,每秒都要获取和发布很多聊天信息。大概每秒600次,包括地图和聊天信息。这些聊天信息很小且可以通过一个socket连接来搞定,但需要经常发布到其他聊天者中去。这就需要合适的选型,如果选错就是灾难。
GoSnaps另一方面需要每秒获取很多图片和赞。这些snaps在服务器上存着,老的snap也是相关的,而老的聊天不是。真正的图片文件是在Google的云存储上,这种请求图片文件数量不是我的顾虑。Google云很可靠,我担心的是在地图上请求的snaps,GoSnaps有个图片识别软件去查看所有上传的特征,检查图片是否与Pokémon GO相关。然后重组了图片并发送到云存储。这些考虑到CPU和带宽都是很重的操作。比复制发布小的聊天信息要重,但没那么频繁。
结论是这两个APP在架构复杂性上都很类似。GoChat处理更多小的消息但GoSnaps处理大图片和更重的服务端操作。设计这两个App的架构需要不同的方式,但复杂度类似。
GoSnaps是作为MVP来开发的,不是一个商业化成熟的产品。就是在24小时内完成。我用了编程马拉松中NodeJS项目,MongoDB数据库也没任何缓存。没有Redis,Varnish,也没有复杂Nginx设置。这个iOS App就是用Objective C写成的,还有借用了些苹果地图相关程序代码。
我愿意考虑MVP去做一个功能的APP,越快越好,不管技术后端质量。图片存哪里?就是MongoDB数据库。这也不需要什么配置和代码。怎么在范围内查询Snaps和拿到最多的like数?就是在整个上传的列表中跑一个MongoDB的查询并且在一个数据库连接中去做一个查询。但所有的这些都可能把我的APP和功能给弄砸。
查看那些我需要拿到Snap的查询:“找到在多边形[A、B、C、D]这个区域内的所有Snaps,去掉一些舍弃的snaps,去掉一些已经用过的,根据赞的数量,合法的Pokémon Go,以及新鲜度排序这在小规模下是可以的,但在很大的负载下将是灾难。即使我把上面的查询变成只有三个条件的操作,还是很恐怖。因为这不是数据库应该这么用的。数据库每次只能查询一个索引,而这种GEO查询是办不到的。如果你没很多用户也就算了,但像做到Gochat这种挂了就说不过去了。
我怎么做的呢?在把CPU计算很重的图像识别和大小重组后,这种处理之后的照片就上传到Google的云存储上。这样服务器和数据库在图片请求时都不会受影响。数据库要考虑数据,而不是图片。这也节约了服务器。在数据库方面,我把snaps分成不同的类型:所有snaps,最欢迎的snaps,最新的snaps,最新可用的snaps等。当一个snap加入后,被喜欢或者标记成丢弃,代码会检查是不是属于某一类集合并做适当操作。这样代码可从准备好的集合中去查询而不是去在一个巨大的列表中去跑复杂查询。这就把数据从逻辑上隔离划分到桶中。也可以去排序查询做地理关联,把选择数据给简化了。
这些多余的事情花了多久呢?大概2、3个小时吧。为什么我要先做这些,因为这些就是我做事的方式,我认为这个APP会成功,也没有理由说开发了一个APP就假设它不成。如果我的APP获得一些口碑但因为技术不好而挂了,我是不能容忍的。我把最小可见规模化的原则植入到APP中。这就是快乐和痛苦的区别,也是成为一个APP MVP中的一环。
如果我用一个更慢的编程语言或者大的框架,我可能需要更多服务器。如果我使用PHP跟Symfony,Python跟Django、Ruby on Rails,我可能花整天去解决APP中最慢的部分或者增加服务器。这些语言和框架在很多范围内都很棒,但对于很低成本的服务预算还是不合适的。也许是因为很多代码都是用在把数据库记录映射到框架的逻辑中。这会对CPU很有害,下面给个例子。
之前说过,GoSnaps使用NodeJs作为后端语言和平台,这样快速高效。我使用Mongoose作为ORM(对象映射模型)让MongoDB更像个程序员的写法。
我也不是Mongoose专家,知道这个库有很多代码,所以Mongoose有缺陷的。在上周末,我们的服务器4个NodeJS进程每个都是90%的CPU,这对于800-1000的并发用户是不能接受的。我意识到Mongoose在获取数据的时候做了些事情。我选择使用Mongoose的更轻量的功能去获取普通JSON而不是复杂的Mongoose对象。
改了之后,NodeJS进程降到了5-10%的CPU使用率。所以说知道你代码实际做的事情很重要,把负载降了90%。想象如果是个复杂的库,比如Symfony和Doctrine,仅仅是执行这些代码就需要花很多CPU核,更不要说数据库成为瓶颈了。
选择一个简洁和快速的语言对于扩展性很重要,除非你在服务器上花费很多。选择一个有很多有用库的语言更重要,因为你想快速搭建MVP。NodeJS,Scala和Go都是符合这些条件的好语言。他们提供了很好的工具和性能。像PHP和Java本身也不慢,但由于大型框架和代码库一起用让应用非常笨重。这些语言对于面向对象的开发和很规范测试的代码是很好,但不够快和方便扩展。我这里也不想去引入编程语言之争,个人喜欢Erlang,但在MVP中没用过。
几年前,我创立Cloud Games,是HTML5的游戏运营商。开始时候,是B2C的网站,我们花了很大精力去获取用户而且几个月后也拿到100万的月活用户。那时候,我使用PHP、Symfony2、Doctrine和MongoDB作为简单精炼的基础结构。我也在Spil Games工作过,当时2亿的月活,使用的是PHP,后来迁移到Erlang。当Cloud Games成长到10万的月活时,我们开始关注Doctrine和MongoDB的服务器瓶颈,因为在PHP库有很大负载。我也正确了设置了MongoDB,索引和查询,但服务器在处理能力上还是很难受,后来又做了PHP的APC缓存。
因为Cloudgame.com依旧以静态为主。我花了几天迁移MVP到NodeJS和Redis。同样的设置,不同的语言。这使负载降低了95%。里面有很多要跟PHP库打交道的,但一个最小化的NodeJS比最小化的PHP要容易得多。特别是MongoDB和前段代码都是100%的JavaScript,就像NodeJS,没了框架和库的PHP简直是另一种语言。
我们需要一个干净的环境,因为我们是自己投钱de早期创业团队。Cloud Games后来做得不错并且依然基于NodeJS的高效架构上。我们可能没有管理过一个更节省的技术结构,我们也经历过很多坎坷的创业历程。设计一个低成本、高扩展的架构对成功非常关键。
如果你的App因为一些热点和媒体曝光有机会爆发式增长,一定要考虑你的MVP的扩展性。最小可行产品和可扩展的技术是可以共存的。没有比搭建一个成功App但后来因为技术原因失败更惨的事情了。Pokémon Go自己也有很多问题,但它很独特而且成长足够快。而小型创业公司就没有那么奢侈了。时机就是一切,100万GoChat用户和50万GoSnaps用户的不同结局就证明了一切。
转载请联系授权并保留出处和作者,不得删减内容。