第1节: 崩溃的厨房
在创业旅程中解决第一次重大危机之后的一段时间通常比较暗流涌动,往往预示潜在的危险。
数据库分离之后,我们的系统运行良好,忙碌而有序。应用的响应体验很快,服务器也很稳定,以至于我们竟然产生了某种错觉:我们的架构足以应对未来可能出现的业务量爆发。就好像我们成功为网站的基础架构实施了开胸手术,让它起死回生。甚至是妙手回春。
我们的日常工作也从慌乱的紧急救火转向充满乐观的系统巡视。我们可以实时看到用户数量在攀升,关注服务器的负载图表(系统压力令人喜悦的正常且运行稳定),这些都让我们感到颇为满足。我们成功构建了一套真实有效能解决用户痛点且能实现业务增长的电商系统。我们似乎在某一瞬间觉得自己所向披靡。
不出意外的是,出乎我们意料之外的下一场危机悄然开始。
这次不是之前那样缓慢的性能下滑,而是一次突然的爆发。苏拉特市(Surat)的一位售卖精美手作纺织品的网红店家,在一个人数众多的脸书(Facebook)聊天群里分享了她家的Dukaan店铺链接。与此同时,还有一位技术博主也撰文提到了我们的公司。这两次曝光合力形成了一次完美的增长风暴,让我们体验了从未见过的海啸般的流量。
我的手机开始发出熟悉的警报嗡嗡声。这次不是苏米特打来的电话,而是来自我们的服务器监控系统。“应用服务器CPU占用率超高。”其中一条警报显示。一分钟之后,另外一条警报显示了相同的内容:“危险:CPU占用率已持续5分钟达到100%。”
几乎同时,我还没得及打开笔记本电脑,苏米特的短信就发过来了。短信内容简短,熟悉的沮丧溢于言表。
“发生什么了?”
我马上SSH登录进服务器。手指飞快敲下我最信任的诊断工具:htop。我首先检查的是数据库服务器。看上去一切正常。CPU占用很低,内存使用也没正常。我们新建的图书馆表现安静从容,井井有条地处理着接收到的所有书籍(数据)查询请求。
接下来我继续检查应用服务器。然而这里就完全是个血流成河的场景了。CPU占用率一直处于100%。进程列表放眼望去,已被Gunicorn的工作进程几乎占满,它们面对如洪水般涌入的用户请求,正陷入不断重试然后失败再重试的恶性循环。应用服务器已经完全过载了。虽然还没宕机,当时已经几乎毫无响应。对于外部用户来说,Dukaan网站再一次崩溃了。
确认瓶颈:独自面对1000位顾客的主厨
原因很明显:我们的厨房扛不住了。
延续前面几章的类比,我们之前的优化是在厨房边新建了一个现代化的储藏室/图书馆。这样我们的主厨不再需要担心储藏室的管理难题。现在的问题是,有1000名顾客同时挤进了餐厅,每一个都高声叫嚷着希望主厨先做自己点的菜。
我们唯一的主厨(应用服务器)尽管此时已经因为数据库的分离变得更加高效,但面对1000名顾客的同时点餐,仍然力不从心。现实世界中,一位厨师同时能准备的菜肴显然是很有限的。对于我们的应用服务器来说,它已经触碰到了自身的能力天花板。点餐的排队甚至都要排到了餐厅门外,整个餐馆不得不暂停取号。
很明显我们需要更多的厨师。怎么办?这就引出了每一个业务快速增长的公司都需要面临的关键抉择。这是方向迥异的两条技术路径:垂直拓展或者水平拓展。
深入技术细节:垂直(Vertical)与水平(Horizontal)拓展
当服务器不堪重负时,通常有两个选择。
- 垂直拓展(性能升级Scaling Up)
这可能是一种最容易想到的办法。如果厨房动作太慢,那就把厨师换成一个能以两倍效率工作的世界顶级大厨。
对于服务器而言,就是性能升级:在DigitalOcean上面点击关闭当前服务器的按钮。然后选择一个更强大的付费套餐:用8个CPU加16GB内存取代2个CPU和4GB内存。完工,现在应用就运行在一台性能猛兽上了。就好像把家用小轿车换成了一辆超大型的卡车巨兽。
- 优点:操作很简单。无需更改代码或者架构。只需要多花点钱就可以让问题大事化小。
- 缺点:这个方案有3个不足:
- 很容易导致开销飙升。两倍性能的服务器的价格不一定是两倍。有可能是4倍或者8倍。价格往往指数增长。
- 这种方案上限不高。服务器的性能总有极限,不可能无限升级。最终云服务商所能提供的最强大最贵的服务器也无法满足需求。那到时该怎么办?没有更大的巨型卡车可供选择了。
- 再强大的服务器也是一个单点(Point of Failure)。这是最关键的不足。虽然拥有一台看上去足够强劲也足够贵的服务器,但如果它发生硬件故障,或者因为系统安全补丁需要重启,整个业务都跟着下线了。这就像整个餐厅都完全依赖唯一的一位超级大厨。如果大厨生病了,餐馆就得关门歇业。
- 水平拓展(服务器集群Scaling Out)
这个方法虽然不如前者那样容易想到,但却是更加强大的一种选择。与其重金重新聘请一位超级大厨,不如保留当前的厨师然后再请3位类似水平的厨师。这样让他们同时一起工作也能加大厨房的产能。
对于服务器而言,这就是集群策略:用一个小服务器的规模集群替代单一的大服务器。也就是用4辆常规的家用轿车替代巨兽卡车。
- 优点:
- 性价比很高。小型服务器的硬件通常不贵且易于替换。增加同配置的小型服务器成本可控,不会很离谱。
- 理论潜能不设限。如果需要更多处理能力,再增加一辆小轿车就行了。可以轻松地从4台服务器拓展到40台,甚至400台。横向拓展的系统就天生适合如此。
- 容错率高。这是水平拓展的超能力。即使有1位厨师因病回家(一台服务器宕机),剩下3位也能继续工作。这样虽然出餐会慢一点,但至少餐馆不至于关门歇业。单点隐患不复存在。
- 缺点:系统会变得更加复杂。比如当有服务员拿着顾客新点的单来到厨房时,那这个菜该交给厨房里四位水平相近的哪位厨师来处理呢?这个决定该如何做出?
我们的选择显而易见了。垂直拓展更像一个创口贴式的临时方案。它不适合作为长期的技术选择。我们希望建立的是可以服务数百万用户的公司,所以我们需要一个能够随着业务增长而演进的技术架构。水平拓展是我们的必修课。
既然决定了,那就得开始打造我们自己的服务器集群。也就意味着我们需要解决新架构带来的新问题。我们需要一个能够在厨师团队中高效合理分配顾客订单的新系统。
我们需要一个流量交警:负载均衡。