内容简介:一般来说,我这里的基础设施得益于 CDN、OSS 和网关层,到了这里都不用扛太多的流量,只要写的不怎么车祸,后端都不存在什么性能瓶颈。但是这次要上一个灰度功能,需要和业务相结合,在此之前我们的灰度都是以 DNS 为标准进行地区式的灰度,但是现在流量必须要直接打到后端才能判断,如果前面缓存了就没办法灰度了——因此没办法,我们只能船到桥头自然直——扛吧。先来简单描述一下我们的代码逻辑:查询 1 -> 处理逻辑 -> 查询 2 -> 查询 3 -> 处理逻辑 -> 灰度判断 -> 返回。
一般来说,我这里的基础设施得益于 CDN、OSS 和网关层,到了这里都不用扛太多的流量,只要写的不怎么车祸,后端都不存在什么性能瓶颈。
但是这次要上一个灰度功能,需要和业务相结合,在此之前我们的灰度都是以 DNS 为标准进行地区式的灰度,但是现在流量必须要直接打到后端才能判断,如果前面缓存了就没办法灰度了——因此没办法,我们只能船到桥头自然直——扛吧。
先来简单描述一下我们的代码逻辑:查询 1 -> 处理逻辑 -> 查询 2 -> 查询 3 -> 处理逻辑 -> 灰度判断 -> 返回。
最初保持了最原始的代码,没有设置任何缓存,这在过去是满足需求的,因为缓存是由网关层的反向代理帮我们在 Header 里加上的,包括了 s-max-age 和 max-age。
然后开压: wrk -c20 -t20 -d1m
:
20 threads and 20 connections Thread Stats Avg Stdev Max +/- Stdev Latency 195.58ms 73.94ms 592.81ms 68.39% Req/Sec 5.59 2.83 20.00 63.45% 6155 requests in 1.00m, 3.94MB read Requests/sec: 102.40 Transfer/sec: 67.19KB
最初我们以为是压得连接数太少,增大了之后 QPS 不增反降,跟做网关的大佬交流了一下之后知道了以下两点:
- QPS 并不是压多少有多少,还要看服务器的处理能力
- CPU 和内存都只用了百分之十几,说明瓶颈不在这里,很有可能是 IO
而我们的语句中 IO 的部分只有网络 IO 的数据库操作,于是我们为数据库操作加上了一层 LRU 缓存,大致是这样的:
let result if (cache(query) is not empty) { result = cache(query) } else { result = await db(query) } cache.set(result)
当然,这只是一段伪代码。
然后我们再压了一次:
2 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 337.68ms 485.34ms 2.00s 84.04% Req/Sec 79.44 31.82 323.00 81.98% 9237 requests in 1.00m, 5.92MB read Socket errors: connect 0, read 0, write 0, timeout 4952 Requests/sec: 153.71 Transfer/sec: 100.92KB
性能提升很有限(实际上上面这段是有问题的,读者朋友先思考一下,之后再说),这个时候压测的同学忍不住了,开始和我一起 Review 代码,我们又开始怀疑了连接池的大小。
我们将连接池的最大连接数扩大了十倍,接着压,提升依旧不明显。
然后我们进行了本地调试,开始打日志之后发现 SQL 语句在有缓存的情况下依旧进行了重复请求,这个时候才回想到因为我们没有合并请求(也不好合并请求,因为 querystring 不同),所以并发的数据进来如果没有处理完,其实此时还没有存到 cache,别的请求会接着查询,直到第一波查询完毕,严重的影响了 QPS。
所以我们把查询的 Promise 缓存,而不是请求的结果。(实际上之前做过这种操作的项目,但是一时没想起来)
let resultPromise if (cache(query) is not empty) { resultPromise = cache(query) } else { resultPromise = db(query) } cache.set(resultPromise) result = await resultPromise
压测:
2 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 206.88ms 305.24ms 2.39s 89.53% Req/Sec 1.46k 580.86 2.65k 71.35% 28138 requests in 10.10s, 18.00MB read Requests/sec: 2785.71 Transfer/sec: 1.78MB
并且监控中 CPU 和内存占用也比以前更低了(不过当然没有 QPS 明显拉),系统能在同时处理 1500% 的请求了。
当然,为了避免缓存永远被使用,我们这里设置了五分钟的超时缓存,并且仅当没有缓存时才执行 cache.set
(因为 set 会刷新缓存时间计数器),也就是说,每五分钟才请求一次,这样的频率相信也是我们的用户(业务方)能接受的范围内。
以上所述就是小编给大家介绍的《一次让性能提升 1500% 的经历》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Python高级编程(第二版)
[波兰] Michał Jaworski、[法] Tarek Ziadé / 张亮、阿信 / 人民邮电出版社 / 2017-9-19 / 89.00元
Python作为一种高级程序设计语言,凭借其简洁、易读及可扩展性日渐成为程序设计领域备受推崇的语言之一。 本书基于Python 3.5版本进行讲解,通过13章的内容,深度揭示了Python编程的高级技巧。本书从Python语言及其社区的现状开始介绍,对Python语法、命名规则、Python包的编写、部署代码、扩展程序开发、管理代码、文档编写、测试开发、代码优化、并发编程、设计模式等重要话题......一起来看看 《Python高级编程(第二版)》 这本书的介绍吧!
CSS 压缩/解压工具
在线压缩/解压 CSS 代码
在线进制转换器
各进制数互转换器