翻译-新手向大型网络应用扩展

前言

上个星期就准备翻译出这篇文章来着,但是毕设答辩加上清明节假期就导致拖到了今天。

话不多说,这篇文章是在阮一峰老师的每周记录中看到的。因为之前看了顶天的《大型网络扩展和JAVA中间件》这本书(2/3),所以对这个话题比较熟悉。加上这篇外文有很好看的插图,清晰易懂,没有技术性的阅读障碍,是通过逻辑性的思维进行网站应用扩展的讲解,所以我把它翻译出来,复习使用。

正文

这篇入门总结了将单服务器扩展到百万级别用户大型网站扩展的基本原则。这篇文章针对的是,技术领域的新手或者非开发人员的,所以如果你正在部署更高大上的应用服务的话,这篇文章并不适合你。

如果这正是你想要的,那么开始吧。

什么是扩展

你把你编写的网站,网上商店、社交网络等,部署上线,一切都运行的很美好:每天都有几百用户访问、请求响应及时、调用立即完成。一切都有条不紊地不断运行。

但是一些”不好”的事情发生了:你大获成功!

几百、上千、上万的用户每时每秒不断涌入,并且在不断增加。这看起来是商业上最棒的消息了,但同时也在对你的基础设施(服务)说:亲爱的,你大事不妙。因为对于网站的服务来说必须要扩展了,因为它能够:

  • 同时为更多的用户提供服务
  • 全天在线,不停机
  • 服务全世界用户

扩展是怎么工作的

以前,这篇文章要以讨论”纵向”扩展(”vertical”)和”横向”(”horizontal”)扩展来开头。总之,纵向扩展指的是在更强大的机器上运行相同的应用,而横向扩展是并行运行多个服务进程。

现在,已经没有人纵向扩展了,理由如下:

  • 计算机的价格和性能成指数增加的关系
  • 单机可以运行很快,但是在纵向上的扩展是有极限的
  • 多核CPU意味着单机也能有效的并行运算,那不如一开始就并行化好了

那么横向扩展的必要步骤有那些呢?

1. 单服务器+数据库


你的网站后端可能一开始就是这个样子的。一个应用服务器执行你的业务逻辑,数据库存储数据。事情简单而美好,但是想要其高可用,只能用更好的机器–这并不好。

2. 增加反向代理

为了使你的架构能够适应更大的扩展,第一步要加一个”反向代理”。它就相当于酒店的前台。当然,你可以让客人直接去自己的房间;但是,你真正需要的事一个中间人,能够确认这位客人是否允许进入、到底有没有订房或者正去往一个正确房间的路上。同时你还要要告知客户,他要的房间可不可用、能不能去,以免尴尬的徘徊。以上就是反向代理的工作。代理就是用来接收和转发请求的。通常情况下,请求是从我们的服务器发出到互联网上的。但是这次,是从互联网上路由到我们的服务器上,所以我们”反向代理”。

一个代理需要完成以下任务:

  • 运行状态检查,确保我们的服务器还在运行
  • 将请求路由(定向)到正确的终点
  • 认证,确保请求的发送方是有许可的
  • 防火墙,确保请求方访问的是服务器上有权限的内容

3. 引入负载均衡

许多反向代理还能够负载均衡。负载均衡器是一个很简单的概念:想象一下,在一分钟内有一百个用户要向你的网上商店付款,很不幸的是,你的支付系统只能同时处理50个支付请求。怎么解决?同时运行两个支付系统服务器。

现在负载均衡器的作用就是将支付请求分摊到这两个服务器上,用户1到左边的服务器,用户2区右边的服务器,用户3去左边的…以此类推。

如果同时🈶500个支付请求怎么办呢?加到10个支付系统服务器,然后用负载均衡分摊请求。

4. 扩展数据库

运用负载均衡可以将压力分在各个服务器上。但是你发现问题了么?虽然我们可能有成百上千个服务器,用来处理请求,但是只有一个数据库存取数据。

所以,我们可以以同样的方式来扩展数据库嘛?很不幸的是,不行。关键在于一致性的问题。我们系统中的每个部分都要确保使用数据的一致性。不一致的数据导致各种棘手的问题:订单为重复执行2次,从余额100的账户里扣除2次90元的付款等等。所以我们怎样才能扩展数据库,同时又保证数据的一致性呢?

首先我们要把数据库分成不同的部分。一部分专门用来接收和存储数据,其他部分专门用来读取数据。这种方案有时称为主/从魔石,有时称为使用只读副本写入。而且假定服务器读数据比写数据的次数更多。
这样的解决方法就能保证一致性,因为数据是从单个接口写入的,而且数据流动是单向的,从写到读。
缺点就是数据输入还是由单个数据库完成,这对于中小型的应用来说是OK的,但是对于像Facebook那样级别的就不行。更进一步的数据库扩展将在之后讨论。

5. 微服务

到现在,我们都用一个服务器去完成所有的工作:处理支付、订单、库存、提供web服务、管理用户账号等。

这未必不是一件坏事,单个服务器意味着更低的复杂度(结构上的),对我们开发者来说没那么头疼。
但是随着扩展的增加,事情开始变得复杂和低效:

  • 不同的服务用在不同的范围。对于用户登录这个流程,可能在很多页面都有,涉及到很多的资源。但是这一些都是在一个服务器上完成的。
  • 我们的代发团队随着应用而成长。但是越来越多的开发者在一个服务器上工作,不可避免的就会干扰到别人。
  • 所有服务都在一起,意味着每次发新版都要将所有服务都停掉。这会导致一个很严重的相互依赖关系,一个团队已经做完了,等着发布上线,但是另一个团队可能才完成一半。

解决这个问题的方法是一个结构范式,它已经掀起了开发界的一大浪潮:微服务。思想很简单,将服务器分成多个功能单元,将他们部署成独立的、互联的迷你服务器。
这样做有几大好处:

  • 每个服务都可以独立扩展,让我们能够机动的适应新需求。
  • 每个开发团队独立开发,对自己的微服务生命周期负责(创建、部署、更新等)
  • 每个微服务运用自己的资源,如数据库(一定程度上缓解了4中的问题)

6. 缓存和内容分发网络CDN

怎么才能工作更高效呢?不要什么都干!
我们的web应用很大一部分都是静态资源,像图片、js脚本、css文件、预渲染的进入页面等。
所以相比较每次都重新请求数据资源,我们不如将一些结果放在简单的缓存之中,谁需要就去拿,不用干扰到后面的服务器。

缓存的大哥叫内容分发网络CDN,是一组分布在全世界范围内的缓存。这让我们能够从用户就近的节点向他们分发内容,不需要每次都翻山越岭了。

7. 消息队列

你去过游乐园吗?你是不是每次都直接去售票厅就能买到票呢?大部分情况都要排队吧应该。
政府机构、邮局和游乐园都是”sub-capacity parallelism”很好的例子。他们是并行的–多个售票厅同时售票。
但是貌似不可能有足够的售票厅能让每个人都能立即买到票,所以队列就形成了。

在大型网站上同样可以套用这个概念。成千上万的图片每时每刻上传到Instagram、Facebook上面,每张图片都要被处理、调整、分析和打标,这是一个耗时的过程。
所以为了使用户不用等待所有流程都结束,服务器接收到图片之后,只完成下面3件事:

  • 存储原始图像数据
  • 告诉用户上传成功了
  • 向一个大堆增加虚拟标签,说明接下来需要做些什么

接下来这些标签会被许多的服务器拿到,这些服务器会完成标签上需要完成的任务,确认之后再返回标签,直到所有的任务都完成。
系统将这一堆标签叫做”消息队列”,运用这个队列有几大好处:

  • 它将任务和处理器解耦。有时许多图片需要处理,有时很少;有时有很多可以进行处理的节点,有时可用的很少。
    通过在代办事务中增加任务标签的形式,而不是直接送过去处理,可以保证我们的系统是响应的,且没有任务丢失。

  • 它使我们能够按需扩展。因为启动服务器是耗时的,如果已经有很多用户上传了图片需要处理,而图片是直接传给服务器的,那么再启动等多的服务器就已经迟了。相反,如果我们有消息队列,就可以扩展处理能力来处理那些需要处理的任务。


👌如果我们的系统已经经过了以上的扩展,那么已经可以应对大流量了。但是如果我们想要更大更强呢?以下是几个选项。

8. 分片/分区

什么是分区?定义如下:

“Sharding是一种通过将应用程序的堆栈分成多个单元来并行化应用程序堆栈的技术,每个单元负责某个键或命名空间”

额,所以到底什么是分区?其实很简单:需要访问20亿Facebook用户的档案,那就把Facebook分成26个小Facebook,每个Facebook服务不同姓名首字母大写的用户。

分区不一定按照首字母划分,可以是地区、使用频率(头部用户给更好的服务)等。
你可以将服务器分区、数据库分许,或者任何你应用系统中的一个方面,只要满足你的需求。

9. 对负载均衡器进行负载均衡

单个负载均衡器只能做那么多,即使你购买超贵超牛的负载均衡器,性能都是有瓶颈的。

很幸运的是,有一个全球性的、分散的、超级稳定的层次,能够在流量到达负载均衡器之前进行分流操作。那就是”域名系统”,DNS。全球域名注册表将”github.com”映射到”XXX.XXX.XXX.XXX”这个IP地址上,同时也允许我们将特定域名映射到多个IP地址上,这样就能到达多个不同的负载均衡器了。


好了,我们做的已经很多了,希望这篇对你有所帮助。但是如果你是IT行业的一员,那么你肯定会问,”到底啥是云服务?”

云计算和ServerLess(无服务器)

什么是云服务?它是对于上面提到的所有问题一个便宜又高效的解决方法,简单来说就是,别自己解决他们。

而是让云服务厂商根据你的系统和需求为你提供扩展服务,你不用考虑任何复杂的问题,不要再造轮子。

后面略…(介绍下一篇文章的东西)