上线 5 天获得 100 万用户,可为什么我开发的 App 最终却倒闭了?
【CSDN编者按】风靡一时的 Pokémon GO 曾经获得过无数游戏爱好者的追捧,上线仅一个月下载次数就高达 13000 万次。Jonathan Zarra 开发的聊天应用程序 GoChat 即是专为 Pokémon GO 粉丝服务的,上线 5 天内就获得了 100 万用户。但是近期,还没等到融资成功,GoChat 却倒闭了。背后真正的罪魁祸首,或是那个每月仅 4000 美元成本、不可扩展的服务器。
作者 | Erik Duindam
译者 | 虎说
责编 | 郭芮
出品 | CSDN(ID:CSDNnews)
以下为译文:
创业公司似乎普遍认为,你应该建立一个MVP(最低可行产品),而不必过多关注技术的可扩展性。我多次听说,只要你的商业模式能够大规模运作,你就会活得很好,而不必浪费时间和金钱来打造技术可扩展的产品——可扩展性是以后的问题。不幸的是,这种有点盲目的信念可能会导致很可怕的失败,例如,Pokemon GoChat。
我想Pokemon GoChat的创造者Jonathan Zarra就吃够了教训。通过为Pokémon GO粉丝制作聊天应用程序,他在5天内获得了100万用户。近期,他一直在与风险投资公司交谈,试图介绍他的发展规划并通过他的应用获利。不幸的是,不久后GoChat却倒闭了。
后来我才知道,Zarra很难为托管1M活跃用户所需的服务器付费,他从没想过会有那么多用户。他将此应用程序构建为MVP,后来才关注可扩展性,但是他失败了。于是Zarra聘请了Upwork的承包商解决了很多性能问题,承包商表示服务器成本约为4000美元。那是2016年,我认为他说的不是4000美元的硬件,而是每月或每年4000美元的虚拟服务器和流量成本。
在我的职业生涯中,我一直在为数亿活跃用户设计和构建网络平台。我可以说,对于聊天应用中的1M用户来说,4000美元是完全不靠谱的金额,即使是MVP,这意味着应用程序的服务器技术设计很差。为月活达到数百万用户构建经济高效、可扩展的系统并不容易。但是,通过利用云上的廉价服务器来处理相当数量的用户却没有很复杂,这些只需要在构建MVP时考虑到。
GoSnaps:5天内获得100万用户,每月只需100美元的服务器
与GoChat类似,我最近也推出了一款名为GoSnaps(针对PokemonGO粉丝)的应用程序。
GoSnaps是一款在地图上分享PokémonGO截图和图像的应用程序,GoSnaps第一天增长到6万用户,第二天增加了16万用户,5天后增加到了100万用户,它现在有150-200k上传的快照。它在任何时间点大概都有大约1000个用户同时在线。
我建立了图像识别程序,以自动检查上传的图像是否与PokémonGO相关,并调整上传图像的大小。我在一台价格为100美元/月的中型谷歌云服务器上运行整个设置,再加上(廉价)谷歌云存储用于存储图像。
GoChat和GoSnap的技术比较
我们来比较GoChat和GoSnaps,这两个应用程序都能基于地理位置,每秒触发大量请求以获取聊天/图像。一般而言,地理空间查找可以是经纬度位置的多边形,也可以是特定点。我们选择前者,每当有人移动地图时都会触发此请求。这些查询是对数据库的重复操作,尤其是与排序或过滤相结合。GoSnap每秒都会收到这种类型的搜索请求数百次,GoChat可能也是如此。
GoChat的独特之处在于它必须每秒获取并发布大量聊天消息。此前曾对外声称整个应用程序每秒有600个请求,这600个请求是地图请求和聊天消息的组合。这些聊天消息很小,可以通过简单的套接字连接完成,并分发给其他聊天者。通常来说,这种频繁分发是可以通过正确的设置进行管理的,但对于类似MVP的糟糕设置却是灾难性的。
而GoSnaps每秒都会获取和“点赞”许多图像。由于旧的照片也有一定的相关性,所以照片需要堆积在服务器上。由于实际图像文件存储在谷歌云端存储中,因此作为开发人员,我不需要考虑所请求的图像文件的数量。谷歌云可以处理这个,我相信谷歌。GoSnaps拥有图像识别软件,可在所有上传中查找图案,以查看图片是否与Pokemon有关。它还会调整图像大小并将其发送到云存储。这些都是在CPU和带宽方面比较的繁重操作,比分发一些聊天消息更重,但频率较低。
我的结论是这两个应用程序在可伸缩性复杂性方面非常相似。GoSnaps处理更大的图像和更重的服务器操作,GoChat则处理更多小消息。为这两个应用程序设计架构时需要两套稍微不同的方法,但同样复杂。
我是如何在24小时内构建可扩展的MVP的?
GoSnaps是作为MVP构建的,它不是专业的商业产品,它是在24小时内完成。我为hackathons采用了NodeJS模版,并使用了MongoDB数据库且没有任何形式的缓存。没有Redis,没有Varnish,也没有花哨的Nginx设置。iOS应用程序是使用原生的Objective-C代码构建的,其中一些是借用应用程序Unboxd的Apple Maps相关代码。
那么我是如何让它可扩展的呢?
假设我们先不管技术后端质量,MVP只是一场与时间竞赛,以尽快建立功能性应用程序。我会把我的图像放在哪里?在MongoDB数据库中,因为它不需要配置,也不需要代码。查找快照只需在整堆上传的快照上运行一个简单的MongoDB查询即可。一个数据库集合上只有一个数据库查询。但,所有这些都会破坏我的应用程序和应用程序的功能。
因为查看快照必须运行以获取这些快照的查询:“查找位置多边形[A,B,C,D]内的所有快照,不包括标记为滥用的快照,不包括仍在处理的快照,按数量排序喜欢,由有效的PokemonGO照片组成。”这适用于小型数据集,但在严重负荷下,这都是灾难性的。即使我将上述查询简化为仅包含三个条件/排序操作,也会是灾难性的。
为什么?因为这不是数据库的使用方式。数据库应该一次仅查询一个索引,这对于这些地理空间查询是不可能的。如果你没有很多用户,你就会侥幸成功,但是一旦你获得成功,你就会失败,像GoChat一样。
那我做了什么呢?
应用图像识别并进行大小调整后,将已调整大小的图像上传到Google Cloud Storage。这样,服务器和数据库就不会因请求图像而受到攻击,而且可以节省许多服务器。在数据库方面,我将快照分成几个不同的集合:所有快照、最喜欢的快照、最新的快照、最新的有效快照等等。每当快照被添加,代码会检查它是否属于其中一个集合并相应地采取行动。这样,代码可以从准备好的集合中查询,而不是在一大堆乱七八糟的情况下运行复杂的查询。它只是将数据逻辑分成一些简单的存储桶。并且允许我通过一个排序操作仅查询地理空间坐标,而不是如上所述的复杂查询。简单来说:它可以直接选择数据。
我花了多少时间在这方面上?2到3个小时。为什么我这样做呢?因为我认为我的应用程序会成功。如果我的应用程序成为爆款,然后由于糟糕的技术而死亡,我将无法入睡。我将最小可行的可伸缩性原则融入我的应用程序。这是我认为应该成为应用MVP的一部分。
为你的MVP选择合适的工具
如果我用较慢的编程语言或大框架构建GoSnaps,我将需要更多的服务器。如果我会使用PHP或者使用Python,或者Ruby on Rails,我会花费很长时间来修复应用程序的性能问题或添加服务器。相信我,我以前做了很多次。这些语言和框架在许多场景中都很棒,但对于服务器预算较低的MVP则不然。让我举一个例子说明这实际上有多重要。
如上所述,GoSnaps使用NodeJS作为后端语言/平台是快速和有效的。我使用Mongoose作为ORM来使MongoDB直接工作,尽管我不是Mongoose专家,但我知道这个库本身就有很大的代码库。在上周末的某个时刻,我们服务器上4个NodeJS进程分别以90%的CPU运行,这对800-1000个并发用户来说是不可接受的。我意识到它必须是Mongoose在用我获取的数据做事情,显然我只需启用Mongoose的“lean()”函数来获取普通的JSON对象而不是神奇的Mongoose对象。在更改之后,NodeJS进程降低到大约5-10%的CPU使用率。我认为了解代码实际执行的操作的简单逻辑非常重要。
选择精简和快速语言对于可扩展性非常重要,而且还能为服务器节省了大量资金。选择具有大量有用库的语言甚至更重要,因为你希望快速构建MVP。NodeJS、Scala和Go是涵盖这两个要求的好语言,它们提供了许多具有良好性能的好工具。像PHP或Java这样的语言本身并不一定很慢,但通常与大型框架和代码库一起使用,这会使应用程序变得繁重。这些语言非常适合干净的面向对象开发和经过良好测试的代码,但不适用于快速和廉价的可扩展性。我不想开始一个大的编程语言论证,所以让我只说这是主观的和不完整的。我个人喜欢Erlang,绝不会将它用于构建MVP。
创业公司Cloud Games的经历
几年前,我联合创办了一个HTML5游戏发行商Cloud Games。当我们开始时,我们是一个专注于MENA地区的B2C游戏网站,我们花了很多精力来获得用户,并在几个月后达到了每月1M活跃用户(MAU)。
当时,我在一个非常简单和精简的设置中使用了PHP、Symfony2、Doctrine和MongoDB。我曾经为Spil Games创造过2亿MAU,当时使用PHP然后转移到Erlang。在Cloud Games达到大约100,000 MAU之后,由于这些PHP库的巨大开销,我们开始看到Doctrine和MongoDB真正的痛苦。我确实以正确的方式设置了MongoDB、索引和查询,但是服务器很难处理所有代码,接着我使用PHP的APC缓存等等。
由于cloudgames.com是静态的,我可以在几天内将MVP迁移到NodeJS和Redis。类似的设置,不同的语言,导致负载立即减少了约95%。当然,这与避免使用PHP库而不是实际语言有关。但是简约的NodeJS设置比简约的PHP设置更有意义,特别是因为MongoDB和前端代码也是100%JavaScript,就像NodeJS一样。没有框架和库的PHP只是另一种语言。
我们需要这种便宜的设置,因为我们是一个自筹资金的早期创业公司。Cloud Games现在表现不错,仍然基于经济高效的NodeJS架构。考虑到我们作为创业公司经历了一段非常艰难的时期,我们可能无法通过更昂贵的技术设置取得成功。设计低成本、可扩展的架构对于成功至关重要。
MVP和可扩展性可以共存
如果你的应用有机会因炒作或媒体报道而呈指数级增长,请务必将可扩展性视为MVP的一部分。
最小可行产品和可扩展技术的原则是可以共存的,没有什么比建立一个本应该成功却因为技术问题而导致它失败的应用程序更令人伤心。
原文链接:https://medium.com/unboxd/how-i-built-an-app-with-500-000-users-in-5-days-on-a-100-server-77deeb238e83
作者简介:Erik Duindam,Everwise的工程主管,Cloud Games的联合创始人兼董事会成员。
本文为 CSDN 翻译,如需转载,请注明来源出处。
热 文 推 荐
☞ 刚刚!程序员集体荣获2个冠军,这份2018 IT报告还说这些!
☞ 云头条 | 华为云发布全新Slogan;AWS推出DocumentDB;FRB信号刷屏
☞ Grin带火的MinbleWimble技术,到底是个什么鬼?
print_r('点个好看吧!');
var_dump('点个好看吧!');
NSLog(@"点个好看吧!");
System.out.println("点个好看吧!");
console.log("点个好看吧!");
print("点个好看吧!");
printf("点个好看吧!\n");
cout << "点个好看吧!" << endl;
Console.WriteLine("点个好看吧!");
fmt.Println("点个好看吧!");
Response.Write("点个好看吧!");
alert("点个好看吧!")
echo "点个好看吧!"