经验-查询类接口超时优化技巧
| 版本 | 内容 | 时间 |
|---|---|---|
| V1 | 新建 | 2025-04-10 00:27:00 |
| 名词 | 解释 |
|---|---|
| motan | 微博 rpc 框架 |
注意,本篇仅涉及代码层面的优化手段,不包括 redis、MySQL 等优化 rt 时间的手段
背景
当前查询类接口成功率要求达到 99.999%,除了对一些常见接口错误处理过后,剩下的接口基本上都是 motan rpc 超时导致的错误,此时需要对这些超时的方法进行专门处理。
跟踪分析超时方法
使用arthas的trace,可以方便的跟踪方法的执行时间情况,找到耗时最长的方法,依次进行处理。
或者观察 Skywalking 等链路追踪观察节点耗时情况。
常见的优化技巧
减少日志打印
带有查询的方法尽量不要打日志,或者只能打 debug 日志(debug日志使用logger.isDebugEnabled()先进行判断)特别是日志中数据量较大,打印 list 或者 map 的,或者是使用 JSON 转换的大对象的。
循环中日志打印也要注意,如果能在循环外一次打印完成则放在循环外打印,不在要循环中打印。
异味:
public void doSth() {
List<Data> list = ...
for(Data data : list) {
doNestSth();
}
}
public void doNestSth() {
log.info("this is a log");
}[!WARNING]
有时候循环嵌套太深了,循环打印日志可能并不好发现
卫语句-尽早返回
及早结束或返回(短路)
处理中,如果数据为 null 或者 list/map 为空,那么就立即进行 return,不要把空或者 null 的数据再去进行下一步的调用,节省时间。
首选批量查询接口
可以批量查询的话尽量使用批量查询,避免循环单个查询(可以扩展到 redis 和 mysql 的查询)
批量查询入参去重
批量查询注意去重
代码中的批量查询,查询的 id 如果可以去重的话,都进行去重操作(stream 的 distinct)
特别是非常容易重复的地方,比如各种数据流中 uid 的去重,动态下标签的去重等。
public List<Data> batchQuerySth(List<Long> idList) {
// 去重
idList = idList.stream().distinct().collect(Collectors.toList());
// 最真正的批量查询动作
doBatchQuerySth(idList);
}并行调用与结果聚合
合理使用异步查询
如果代码中要调用多个查询方法,每个查询方法耗时都长,那么使用线程池并行处理这些请求是个好方法。
public Object querySth() {
ExecutorService executor = Executors.newFixedThreadPool(1);
Future<Data1> data1Future = executor.submit(() -> {
// 查询动作1
});
Future<Data2> data2Future = executor.submit(() -> {
// 查询动作2
});
// 组装数据 ......
}类似的还有 CompletableFuture
本地缓存
对于经常查询但是变化很少的数据进行本地缓存(guava、caffeine)
需合理使用
缓存数据预热
可预知的数据提前预热到缓存(本地缓存、redis)
例如:某主播开播前,客户端需要从服务端获取大量数据进行渲染。但是这些数据的组装,填充缓存这些动作都比较耗时。主播开播需要的数据是可预见的,可以再主播开播前提前加载到缓存。
最小化查询数据
针对自己想要的数据选择合适的查询方法
比如查询用户信息,如果只想获取用户的昵称/头像/性别的基本信息,而不需要勋章/刻印等,那么可以直接使用仅查询基本信息的接口。
需要接口提供者进行兼容
(这里也是个设计原则:写出“小”的代码,然后在进行多个“小”代码的组装。而不是直接写出“大”代码,调用方可能不需要多余的数据但却无可奈何)
rpc 重试次数
调整 motan rpc 查询的重试次数
默认重试次数为 0,可以调整为 1
<motan:method name="checkUserV1" argumentTypes="java.lang.Long,java.lang.Boolean" retries="1"requestTimeout="500"/>调整 rpc 超时时间
如果经过多种优化措施后,耗时还是很多,那么可以调整超时时间
`<motan:method name="getBatchUserInfo" argumentTypes="java.util.List,com.weibo.zb.biz.commons.model.ThirdChannelType,java.lang.Boolean" requestTimeout="5000"/>接口整合与废弃数据清理
将查询接口整合,不用的数据删除,重复的数据去重。
HTTP 调用重试
通过 HTTP 调用的查询接口,适当增加重试比如调用微博的广场流/H5的查询/阿里云的查询等