作为后端工程师,潜意识就是编写接口、分层缓存、机器扩容、线程满度等一系列组合准备工作,而且因为数据更新的频率可以双手数,所以我们采用了最安全的方式进行处理,直接生成静态文件来承担CDN的阻力。
架构过程如下所示:
数据更新后,会重新生成新一轮的文件,刷新CDN时会触发大量回源请求,极端情况下应用服务器必须持有9W QPS
双机房共有40台4C机,25KB数据文件,5W QPS直接打CPU到90%。
这显然不符合业务需求,我该怎么办? 让我们先尝试无脑和机器。
这时,测试同学反馈压力测试的数据不正确,上一轮最大文件大小为125kb,这让事情变得更糟。
结果机器数量翻了一番,达到80台,服务端CPU仍是瓶颈,QPS无法提升。
最后,它消耗了 CPU 资源,而且整体架构非常简单,再简单不过了。
这时我们注意到,为了节省网络带宽,nginx开启了gzip压缩,这小子是干的吗?
server
为了验证这一假设,我们将 nginx 中的 gzip 压缩比从 6 调整为 2,以减少 CPU 上的计算量。
gzip_comp_level 2;这一轮压下CPU还是很快被填满了,但QPS勉强达到了9W,这证实了确实是gzip消耗了CPU
nginx 作为知名的 Web 服务器,以高性能、高并发著称,仅凭一个静态数据文件就把应用服务器压得这么高,一定是错的。
在明确了 gzip 正在消耗 CPU 之后,我们深入研究了相关信息并发现了一些进展。
HTML css js 等静态文件通常包含大量空格、标签等重复字符,重复的部分可以用距离加长度来表示,以减少字符数,从而大大降低带宽,这是 gzip 无损压缩的基本原理。
作为一种端到端的压缩技术,gzip 约定文件压缩在服务器端完成,在到达客户端之前,它在传输过程中保持不变。 这不是一个好的理论基础
nginx 中的 gzip 压缩有两种类型:动态压缩和静态压缩。
动态压缩。 当服务器向客户端返回响应时,它会消耗自己的资源进行实时压缩,以确保客户端获得 gzip 文件。
此模块默认编译,详情可查看
静态压缩。 直接转换预压缩的.gz 文件将返回到客户端,并且该文件不再实时压缩,如果 .gz 文件,将使用相应的原始文件。
此模块需要单独编译,详情可以查看
如果 gzip static 一直处于开启状态,并且客户端不支持 gzip,也可以在服务器上安装 gunzip 来帮助客户端解压,这里我们不需要。
我检查了 jdos 自带的 nginx 是否编译了 ngx http gzip 静态模块,省去了重新编译的麻烦。
接下来,通过 gzipoutputstream 在本地生成一个额外的 ..gz,nginx 配置再次进行静态压缩。
gzip_static on;
面对9W QPS,40台机器只使用了7%的CPU使用率。
为了继续加大底层压力,应用服务器的CPU增长缓慢,直到网络出口速率拉到89MB s,QPS已经达到27W,生怕影响主机上其他容器的压力
QPS 5W->27W速度提升5倍,CPU 90%->7%提升10倍,整体性能提升50倍以上
经过一系列的分析实践,似乎静态压缩具有“压倒性”的优势,那么哪些场景适合动态压缩,哪些场景适合静态压缩呢? 经过一番**,得出以下结论。
不变的纯静态文件适合静态压缩,应提前使用 gzip 压缩,以免浪费 CPU 和带宽。 动态压缩适用于API接口返回给前端数据等动态场景,数据会发生变化,因此NGINX需要根据返回的内容对数据进行动态压缩,以节省服务器带宽。作为后端工程师,Nginx 是我们的老熟人,我们抬头看都看不到对方。 在日常工作中,配合匹配的**规则,检查标题设置,基本使用nginx作为反向**。 这次是直接接入静态资源,调优过程中的一系列优化,加深了我们对gzip动态压缩和静态压缩的基本理解,这在老枪眼里看似微不足道,但对我们来说却是一次难得的扩展技能的机会。
在之前的职业生涯中,我们一直专注于业务架构的设计与开发,性能的优化似乎已经形成了一种心理惯性。 面对大数据量和长事务请求,减少循环和批处理次数,增加并发,增加缓存,不走就解决异步任务,一般瓶颈出现在iO层面,毕竟磁盘速度慢,减少与数据库的交互次数往往会有效果, 其他概率都不是问题。这次有点不同,CPU被揍的原因是数据计算量大,在高并发请求之前,任何环节都可能造成性能问题。
作者:京东零售闫闯。
*:京东云开发者社区**转载请注明**。