接口请求优化,降低服务器压力
背景
手炒-资产分析页面 2023.01.03 上线新的功能支持用户自定义水印,所以页面初始化的时候会去发起请求获取自己账户之前设置的自定义水印的文案
但是后面发现交易日的开盘和收盘阶段,用户会集中访问这个页面,但是服务端压力过大
服务端和运维一块排查后,提出对部分请求量较大的接口进行请求优化,获取自定义水印和获取用户昵称的接口就在其中。
先看优化前的请求量:
从服务端的监控看板可以获知,在每天开盘收盘阶段,单单自定义水印这个接口每一分钟的请求量就接近 7w,即单是这一个接口的 QPS 就接近 1000
,而资产分析这个页面初始化时会发起 12-16 个接口请求(根据用户设置,请求的数量会变化)。
服务端预计这个接口优化后能降低整个服务器在峰值阶段的请求量约 6%
。(用户昵称和水印的请求量差不多)
另外之前我们的自定义水印是和账户绑定的,同时一个账户支持登录多个设备
。
方案设计
方案一:客户端本地缓存用户设置的水印和水印是否开启的状态,减少 http 请求,等到用户修改水印时在更新缓存
缺陷:由于我们支持多设备登录,那么就存在 a 设备更新了水印,但是 b 设备不知道水印更新了,仍然使用之前缓存的水印,那么就会造成同一个账户在不同设备上显示不同的水印,甚至一个设备开启了水印,一个设备关闭了水印这种情况
方案二:特定时间段使用缓存,其余时间段保持现状,即在请求高峰期不考虑水印的时效性,优先考虑性能
缺陷:在特定时间段内可能出现水印不同步问题,即 a 设备修改后 b 设备需要等到过来特定时间段后才能正常
最终实现
和运维确认之后,发现请求主要集中在盘前盘后10来分钟内,所以暂定在这两个时间段内:
- 09.15 - 09.45
- 14.14 - 15.15
具体实现流程如下:
- 用户本地无缓存
- 请求水印,请求成功保持到 localStorage
- 下次进入页面,判断 localStorage 是否有缓存,同时当前时间是否处于特殊时间段内
- 是,直接使用缓存
- 否,请求接口,请求成功把数据存在 localStorage
这样不仅能最大程度的减少不同设备之间的差异性,也能避免高峰时间服务端的压力
demo 实现
ts
import dayjs from 'dayjs'
const fetchWaterMark = async () => {
const now = dayjs().format('HHmm')
const openStart = 915, openEnd = 945
const closeStart = 1445, closeEnd = 1515
const cacheWaterMark = localStorage.getItem('waterMark')
const isInMorningPeak = now >= openStart && now <= openEnd
const isInAfternoonPeak = now >= closeStart && now <= closeEnd
if (cacheWaterMark && (isInMorningPeak || isInAfternoonPeak)) {
return cacheWaterMark
} else {
const res = await axios.post('/xxxx/getWaterMark', params)
localStorage.setItem('waterMark', res.data.waterMark)
return res
}
// ...
}
不足
- 多设备账户,如果在特定时间段内修改了水印,那么不同设备上显示的水印可能不一致
- 用户可以通过修改本地时区或时间,从而绕过特殊时间段,导致水印不同步,但是还好绝大部分用户是在中国,时区基本不会变