BestBlogs 3.0 面向中文阅读的站点"读见"即将内测,发布 v3.0.7 后专栏内容服务端取数 P50 和 P90 明显改善,中位耗时下降约 81%,后端请求全部落在同机房节点。排查发现专栏列表单次请求触发 240~470 次 MongoDB 查询,且约 43% 流量落到跨机房节点(跨机房往返约 72 毫秒,同机房不到 1 毫秒),通过批量读取、调整部署路由、加缓存和并行请求收敛了等待。
最近,读见站点即将开始内测。它是 BestBlogs 3.0 面向中文阅读的站点,底层服务与 BestBlogs 共用。
我和 @puliandc 测试的时候,一个很直接的感受是:页面很卡。打开专栏、内容页面、进入工作台,都能明显感觉到停顿,还是会影响阅读的体验。
今天顺着整条请求链路做了一轮梳理和排查,也落地了一批改动。把过程记下来,方便分享,也方便以后回看。
1. 先看页面在等什么
从最基础的原理看,一个页面从点开到正文可读,要经过连接建立、网络传输、服务处理、资源下载和浏览器渲染。中间还可能有排队,以及一个请求等待另一个请求的情况。
沿着实际链路往下看,请求会经过 Next.js、Nginx、后端 API,再按需要访问 Redis 和 MongoDB。数据回来以后,浏览器还要处理字体、样式和脚本。
这些环节里,有的耗时来自网络距离,有的来自处理逻辑,有的来自重复执行。一次请求要走多远、来回多少次、哪些事情在串行等待,是这次排查时一直在问的几个问题。
最初的检查里,交互响应本身还可以,比较明显的等待集中在首屏和取数。所以先从服务端日志、接口耗时和机器之间的网络延迟查起。
2. 查了多少次,走了多远
先找到的一处问题,是专栏列表的读取。
一个列表请求,会触发 240~470 次 MongoDB 查询。列表里逐项补充数据,形成了典型的 N+1 查询。单看每次读取,可能都不算重,放到一次完整请求里,耗时就积累起来了。
这部分改成批量读取,只取页面需要的字段,再核对并补齐相关索引,数据库往返收敛到了个位数。
3. 接着看部署位置,又发现了另一层等待。
机器之间的网络延迟呈现出很明显的差别:同机房往返不到 1 毫秒,跨机房大约 72 毫秒。原来的后端路由里,约 43% 的流量会落到跨机房节点。
把调用次数和网络距离放在一起看,很多现象就容易解释了。一次请求本来就要往返很多次,如果其中一些读取还要跨机房,等待就会被进一步放大。
随后调整了部署和路由:把主要入口迁到与数据节点同机房的位置,让在线 API 尽量靠近数据库,慢节点保留为备用;同时把之前合并部署的 RSSHub 单独部署,减少它与在线服务之间的资源争用。
4. 把重复的等待减下来
后端之外,页面里也有一些反复发生的等待。
比如身份验证,之前一次大约需要 371 毫秒,切换页面时还会重复执行。一些读操作反复请求没有被复用,部分可以并发处理的请求也在串行等待。
这次给符合条件的读请求加了缓存,对已有写入失效处理的数据做白名单持久化,并把可以独立的请求改成并行执行。
缓存的范围收得比较明确:哪些读取可以短时间复用,数据变化后怎么失效,都需要有对应的处理。写操作和关键状态继续实时执行。
排查过程中,也补上了一些之前不够用的观测:Nginx 日志增加耗时,后端按接口和节点看延迟分布,浏览器侧接入监测。客户端接入阿里云 ARMS,所有性能数据终于可以清晰的看到。
这些信息补齐以后,再遇到某个页面慢,就能沿着链路看,具体等在了哪一段。
5. 发布后,再看一次数据
今天发布 v3.0.7 后,先取了 10 分钟窗口,与此前基线对比。
专栏某一期内容的服务端取数,已经能看到比较明显的变化,P50 和 P90 都有明显的改善,中位耗时下降了约 81%。发布后采集到的后端请求,也全部落在同机房节点。
整体分析下来,需要改的地方还很多,今天落地的一些收益比高的部分,比如合并查询、机房链路调整、使用缓存、并行请求。其实回看也没有什么特别复杂的问题,期间主要是 AI 在帮我读取和分析,我来做出判断,然后将这些检测和优化变成后续优化改进的基础。
读见马上开启内测,这一轮先把几处明显的等待减了下来。剩下的继续用、继续看数据,再一点点修。
来源:ginobefun · x.com