优化Web应用程序100x就像添加99台服务器

2020-11-21 23:10:34

如今,许多技术讨论都集中在扩展Web应用程序基础结构以处理巨大流量方面。

黑客新闻中充斥着有关Kubernetes,分布式系统和数据库复制的文章; GitHub上的“大规模系统设计入门”非常受人欢迎(11.3万颗星),并且充斥着有关内存缓存集群和数据库分片的建议。

使基于Web的记事本应用程序可以处理每天100亿用户的所有令人兴奋的事情中,重要的是不要忘记现代计算硬件的强大功能以及简单的优化可以走多远。

您可能不是Facebook。建立FAANG级别的基础设施并不会使您的公司成为FAANG,这就像货运人员可以通过建造假跑道和木制ATC塔来召唤有货飞机一样。

大型的,可伸缩的群集基础结构有一定的位置,但是它的规模比许多组织想象的要大。

听起来似乎很明显,但是-优化您的应用程序以在1/10的时间内完成请求就像将9台服务器添加到集群中一样。优化时间到1/100(将请求从1.5秒减少到15ms)就像添加99台服务器。

那是一台1U服务器,可以完成两个42U服务器机架的工作,以前是忙于将低效的代码转化为热量。

那可能是一个极端的情况-但是不必要的膨胀是很常见的,而这些好处很简单,例如添加一个精心选择的索引以将常见查询速度提高10倍或100倍,或者在内存中缓存一些很少变化的响应,而不是每次都重新渲染它。

更干净,更可维护的代码和系统-优化通常只是意味着简化和删除不必要的部分

给用户带来更愉悦的体验(如果您可以优化以在单个服务器而不是一堆联网服务器上运行,则延迟通常会更低)

用户更幸福-开发人员可以将更多精力放在解决用户的问题上,而将精力放在基础架构上

当然,在云基础架构的成本和向开发人员支付优化费用之间需要权衡;但是向开发人员付费以建立应用程序集群和数据库复制也要花钱,并且收益和成本节省更少。

用C ++为Weapon Hacker(我的一个附带项目)编写游戏引擎,使我了解到很多现代硬件的真正功能。

来自C#/ Python / Javascript / JIT编译的世界,我早期的将图形显示在本机C ++和DirectX中的实验令人吃惊;编译后的可执行文件很小,可以立即启动,并且可以以每秒60帧的速度在屏幕上放置数十万个对象,而不会费力。

一个60 FPS的目标为游戏提供了大约16ms的时间来完成其在一帧中所需的全部工作。 Weapon Hacker甚至无法与现代AAA引擎相比,但是它会在15毫秒内生成随机世界(持续约一小时的播放时间),并且可以处理包含数千个粒子,物理对象,纹理,字体的帧字形和图形图元,以及声音混合和音乐流,在2010年代的CPU上大约需要5毫秒。

与游戏引擎相比,大多数Web应用程序的工作异常平凡:接收请求(某些HTTP文本)并返回响应(某些HTML或JSON等)。但是,100ms或500ms的请求处理时间很常见。有时返回的信息生成起来很昂贵,但值得一提的是不要忘记最后只是输入一些字节并输出一些字节。

考虑到任务的相对简单性,在大多数Web应用程序中,每个请求(减去网络延迟)后的处理时间应为16ms。

在每月5美元的便宜双核云虚拟机上,每分钟允许大约7500个请求。如果将静态内容卸载到CDN,则可以达到7,500次综合浏览量。这意味着每天1080万,或每月3.24亿的网页浏览量。

当然,流量不会一整天平均分配,并且云计算通常针对突发负载定价。但是,说实话-您的访问量不在324M浏览量附近,对吧?

如果不是这样,就对Notepadly.io尚未成为市场巨头表示哀悼-但是,祝贺您,您可以将时间从多主复制和竞价型实例自动扩展中转移出来,并添加一个很棒的Clippy虚拟助手。您的用户将对此表示感谢。

服务器端优化是一个很大的话题,可能有人会比我做得更好(并且在解释方面),所以这里只是一些一般性的建议。

这可以像在代码段周围放置计时器一样简单,以查看它们执行多少毫秒。或者,在浏览器的开发工具的“网络”标签中查看TTFB(至第一个字节的时间)。

尽管存在100倍的巨大时差,但2ms和200ms的请求似乎与人类的感知相似,因此拥有硬数据是值得的。

运行简单的压力测试以查看您的应用程序可以处理多少流量。附近的VM或开发机上的一个简单的一页python脚本可以轻松启动一堆线程以尽可能快地发出请求。计算完成请求的数量(例如30秒),并观察服务器的CPU,内存和IO使用情况,以查看最大数量的请求。

将压力测试结果与您的日常流量进行比较,并争取足够的净空,以使正常流量的峰值达到约1%的容量(或与您的业务无关的任何容量)。

优化数据库查询-添加一个精心选择的索引,或者删除一个不需要的索引(索引是独立的内部表,因此有时通过删除一个索引可以节省很多存储空间和I / O时间);调整您的WHERE条件以缩小数据库内部必须扫描的行

减少HTTP请求的数量;不要让REST纯粹主义者告诉您每个实体需要一个单独的URL-如果您的应用始终总是同时需要客户和订单数据,请使其成为单个API调用

确保正确设置并调整了Web服务器和数据库-它们具有足够的内存,线程等。

考虑一个快速的单文件数据库,例如SQLite。它并不适用于所有应用程序,但是我发现它处理单个查询的速度比大型客户端服务器DBMS快5倍左右,存储空间约为1/5,管理复杂性则低得多。凭借其性能数字,它实际上可以为大型站点提供动力。它还支持分片方案(例如每个客户一个数据库文件),可以解决某些应用程序的写并发限制。

了解有关代码性能的信息-特别是内存延迟(作为减速因素)的优势以及I / O的高昂价格。从C ++的角度来看,Chandler Carruth对此进行了精彩的演讲。迈克·阿克顿(Mike Acton)关于面向数据设计的演讲是经典之作。

云主机通常使您可以在出现病毒性流量泛滥时轻松升级到性能更高的VM。在AWS上,在我客户的项目中,我们能够通过克隆到AMI映像,启动新实例并重新路由相同的弹性IP来启动功能更强大的EC2实例(反之亦然,整合为更小,更便宜的实例) ,大约需要5分钟。如果冻结数据更改,则在过渡期间甚至可以保持联机状态。

如果您达到了所需的规模,那可能会让您有时间思考群集。

大型分布式计算设置存在时间和规模。但是,现代的CPU和云VM的运行速度异常惊人,大多数Web应用程序的工作异常平凡-您可以通过一些基本的优化而走得很远,并且可能会在整个过程中为整个世界,您的开发人员和用户带来好处。