如何将用户数从1增加到100,000

许多创业公司经历了这一过程:每天都有大量新用户注册,并且开发团队正在努力支持该服务的工作。

这是一个很好的问题,但是Web上几乎没有关于如何准确地将Web应用程序从零扩展到成千上万用户的清晰信息。通常,要么采用防火解决方案,要么消除瓶颈(并且往往两者兼而有之)。因此,人们使用刻板印象的技巧将业余项目扩展为真正重要的项目。

让我们尝试过滤掉信息并写下主要公式。我们将逐步将我们新的Graminsta照片共享网站的规模从1个增加到100,000个用户。

我们将写下来将观众增加到10、100、1000、10000和100000时需要采取的具体措施。

1位使用者:1辆车


几乎每个应用程序,无论是网站应用程序还是移动应用程序,都具有三个关键组成部分:

  • API
  • 数据库
  • 客户(移动应用程序或网站本身)

该数据库存储持久性数据。API服务于此数据及其周围的请求。客户端将数据传输给用户。

我得出的结论是,如果从体系结构的角度来看,客户端实体和API是完全分离的,那么谈论扩展应用程序要容易得多。

当我们第一次开始创建应用程序时,所有三个组件都可以在同一服务器上运行。在某种程度上,这使我们想起了我们的开发环境:一位工程师在同一台计算机上运行数据库,API和客户端。

从理论上讲,我们可以将其部署在一个DigitalOcean Droplet或AWS EC2实例上的云中,如下所示:

话虽如此,如果该站点有多个用户,则突出显示数据库级别几乎总是有意义的。

10位用户:将数据库提高到一个单独的级别


将数据库分为托管服务(例如Amazon RDS或Digital Ocean Managed Database)将在很长一段时间内为我们服务。这比在一台机器或EC2实例上进行自我托管要贵一些,但是借助这些服务,您将获得许多有用的扩展,这些扩展将在将来方便使用:多区域备份,只读副本,自动备份等等。

现在系统是这样的:

100个用户:将客户带到另一个级别


幸运的是,我们的应用程序确实很喜欢第一批用户。流量变得越来越稳定,是时候将客户端移到另一个级别了。应当注意,实体分离是构建可伸缩应用程序的关键方面。由于系统的一部分接收到更多的流量,因此我们可以对其进行划分,以便根据特定的流量模式来控制服务的扩展。

这就是为什么我想与API分开代表客户端。这使得谈论多个平台的开发变得非常容易:Web,移动Web,iOS,Android,桌面应用程序,第三方服务等。所有这些都是使用相同API的客户端。

例如,现在我们的用户最常要求发布移动应用程序。分离客户端实体和API使其更容易。

系统外观如下:



1000个用户:添加负载均衡器


事情进展顺利。 Graminsta用户正在上传越来越多的照片。注册数量也在增加。我们唯一的API服务器难以管理所有流量。需要更多铁!

负载平衡器是一个非常强大的概念。关键思想是我们将平衡器放在API的前面,并在各个服务实例之间分配流量。这是水平扩展的方法,也就是说,我们使用相同的代码添加更多服务器,从而增加了我们可以处理的请求数量。

我们将在Web客户端和API之前放置单独的负载平衡器。这意味着您可以运行多个实例来执行API代码和Web客户端代码。负载平衡器会将请求转发到负载较少的服务器。

在这里,我们获得了另一个重要的优势-冗余。当一个实例发生故障(可能是过载或崩溃)时,我们还有其他实例仍会响应传入的请求。如果单个实例正常工作,那么一旦发生故障,整个系统就会崩溃。

负载平衡器还提供自动缩放。我们可以对其进行配置,以增加峰值负载之前的实例数量,并减少所有用户睡眠时的实例数量。

使用负载平衡器,API级别可以扩展到无穷大,我们只是在请求数量增加时添加新实例。


. , PaaS, Heroku Elastic Beanstalk AWS ( ). Heroku , - API. , Heroku — .

10 000 : CDN


也许应该从一开始就做。处理请求和拍摄新照片开始使我们的服务器负载过多。

在此阶段,您需要使用云服务来存储静态内容-图像,视频等(AWS S3或数字海洋空间)。通常,我们的API应该避免处理上传图片和将图片上传到服务器之类的事情。

云托管的另一个优势是其CDN(在AWS中,此附加组件称为Cloudfront,但是许多云存储服务都提供了开箱即用的功能)。 CDN自动将我们的映像缓存在世界各地的各个数据中心中。

尽管我们的主数据中心可以位于俄亥俄州,但是如果有人要求日本提供映像,云提供商将进行复制并将其保存在其日语数据中心中。在日本下一个要求此图像的人将更快地收到它。当我们处理大型文件(例如需要很长时间才能在整个星球上载和传输的照片或视频)时,这一点很重要。



100,000个用户:数据级别扩展


CDN确实起到了帮助作用:流量正在全速增长。正如他们所说,著名的视频博客作者Maid Mobrick刚刚向我们注册并发布了他的故事。多亏了负载平衡器,API服务器上的CPU和内存使用率一直保持较低水平(正在运行10个API实例),但是我们开始收到很多请求超时的消息……这些延迟是从哪里来的?

仔细研究指标后,我们发现数据库服务器上的CPU负载为80-90%。我们已经到了极限。

扩展数据层可能是方程式中最难的部分。 API服务器可处理无状态请求,因此我们仅添加更多API实例。但是大多数数据库不成功。我们将讨论流行的关系数据库管理系统(PostgreSQL,MySQL等)。

快取


提高数据库性能的最简单方法之一就是引入一个新组件:缓存级别。最常见的缓存方法是将键值记录存储在RAM中,例如Redis或Memcached。大多数云具有以下服务的托管版本:AWS上的Elasticache和Google Cloud上的Memorystore。

当服务多次重复调用数据库以获取相同的信息时,缓存非常有用。实际上,我们只访问数据库一次,将信息保存在缓存中-不再接触它。

例如,在我们的Graminsta服务中,每当有人进入Mobric星级个人资料页面时,API服务器都会从其个人资料中请求数据库中的信息。它一遍又一遍地发生。由于Mobrick的个人资料信息不会随每次请求更改,因此非常适合缓存。

我们将通过密钥将数据库中的结果缓存在Redis中,user:id有效期为30秒。现在,当有人输入Mobrick的个人资料时,我们首先检查Redis,如果有数据,我们只需直接从Redis转移数据即可。现在,查询网站上最流行的配置文件实际上并不会加载我们的数据库。

大多数缓存服务的另一个优点是,它们比数据库服务器更易于扩展。Redis具有内置的Redis群集集群模式。就像负载均衡器1个,它使您可以在多台计算机(必要时跨数千台服务器)之间分布Redis缓存。

几乎所有大型应用程序都使用缓存;这是快速API必不可少的部分。更快的请求处理和更高效的代码-所有这些都很重要,但是如果没有缓存,几乎不可能将服务扩展到数百万个用户。

读取副本


当对数据库的查询数量显着增加时,我们可以做另一件事-在数据库管理系统中添加只读副本。使用上述托管服务,可以一键式完成。只读副本将在主数据库中保持相关性,并且可用于SELECT语句。

现在是我们的系统:



进一步行动


随着应用程序的不断扩展,我们将继续分离服务以独立扩展它们。例如,如果我们开始使用Websockets,则将Websockets处理代码放入单独的服务是有意义的。我们可以将其放置在我们自己的负载均衡器后面的新实例上,该负载均衡器可以根据打开的Websockets连接和HTTP请求数来进行缩放。

我们还继续与数据库级别的限制作斗争。正是在这个阶段,该研究数据库的分区和分片了。两种方法都需要额外的开销,但是它们使您几乎可以将数据库扩展到无穷大。

我们还希望安装监控和分析服务,例如New Relic或Datadog。这将确定缓慢的查询并了解需要改进的地方。在扩展规模时,我们希望专注于发现瓶颈并解决它们-通常使用前几节中的一些想法。

资料来源


这篇文章的灵感来自我最喜欢的高可扩展性文章之一在项目的初始阶段,我想稍微具体化一下文章,然后将其与一个供应商解开。如果您对此主题感兴趣,请务必阅读。

脚注


  1. 尽管在跨多个实例的负载平衡方面存在相似之处,但是Redis集群的基本实现与负载均衡器有很大不同。[回来]



Source: https://habr.com/ru/post/undefined/


All Articles