一、背景
是日也,天朗气清,惠风和畅,宜搬砖!在熟悉新模块的时候,使用抓包工具看了下页面的请求,发现了疑点: 进入一个页面的时候,为什么获取同一类型的数据需要发起多个请求呢? 当然,具体的业务场景是不可描述的,但请求拆分这个话题是值得进行一番探究的。
二、问题
假设,下图是产品老板给的一个交互页面,它需要获取并显示A、B、C三个数据,作为新时代农名工的我们,需要怎么设计这个接口呢?
- 我应该拆分A、B、C三个接口来返回数据吗?
- 抛硬币来选择拆分行不行?
- 拆分的话有什么好处呢?前端大佬会跟我battle吗?
- 不拆分的话有没有什么依据呢?
三、分析
搬砖也应当做到知其然,且知其所以然,这里总结了一些常见的判断节点。
1.图解
重要:仅阐明大概的思路以供参考,实际情况还需根据
实际业务
来做出判断
2.数据加载的时机、触发点是否一致?
如果 Data A
Data B
Data C
在页面中的触发点和加载时机是不一致的,一般是不应该放到一起的,因为可能会查询获取出未来不一定会使用到的数据(数据预加载的情况不在此讨论),给服务端增加不必要的请求。
例如:在答题场景的时候,点击显示答错次数按钮时,才需要对错题次数(DataC
)进行显示,这样 DataC
的加载时机和触发点跟 头像(DataA
)、题目(DataB
) 是不一致的,所以一般不应该合并为一个接口。
3.是否接受部分成功?
如果 Data A
Data B
Data C
是相互独立存在的,那么这三个数据是应该使用三个接口来获取的,因为使用 Data A
不依赖 Data B
和Data C
,那么当获取Data B
和Data C
的接口出现问题的时候,也不会影响到 Data A
的相关逻辑。
例如:在刷题场景中,页面中题目(Data B
)的获取失败,不应该影响用户头像(Data A
)的正常获取。
4.是否来自同一个数据源?
数据源指的是:可以通过单次请求获取到所需要的数据,可以是
mysql查询请求
,也可以是dubbo接口请求
,还可以是三方开放平台的http请求
等。
如果对同一个数据源的数据进行多次请求查询获取,可能会增加不必要的查询、增加不必要的连接等(当然也会存在单次请求时间过长需要切分的场景)。
例如,有一个页面需要展示 语文题目数量(Data A
)、数学题目数量(Data B
)、英语题目数量(Data C
),而如果查询科目习题数量的dubbo接口
是支持批量查询的,那一般情况下就没有必要去查3次,一次查出即可。
5.管控粒度是否单一?(待定)
管控粒度一般指的熔断、限流、替换实现方案等处理的最小单位。
如果粒度是有所细分的,那么接口也做细分的话会更容易进行管理,出现问题的时候也便于做出精细化的响应措施。
例如:批改一篇作文的时候,修辞手法识别结果(Data A
)、错别字识别结果(Data B
)、病句识别结果(Data C
)分别来自不同的服务商,按维度拆分接口有利于我们管理这些维度, 当 修辞手法维度识别(Data A
) 的服务需要降级或替换的时候,不会影响到 错别字识别结果(Data B
) 和 病句识别结果(Data C
) 的请求,直接重试 修辞手法识别(Data A
) 请求即可。
6.从非业务角度分析:拆,还是不拆?
上边的介绍的判断节点是跟业务场景强相关的,那么,如果一直到最后,从业务上看这些数据的请求可以拆,也可以不拆的时候,我们就需要判断这两种选择会给客户端、服务带来哪些影响了。
四、方案比对
1.结论先行
- split:拆分请求
- combine:合并请求 | 客户端等待时间 | 逻辑实现复杂度 | 错误重试的成本 | 服务端的吞吐率 | | :-: | :-: | :-: | :-: | | split ≈ combine | split > combine | split < combine | split ? combine |
2.请求示意
请求拆分与请求合并的示意图: 其中,合并请求在服务端处理的时候会用到一个线程池
3.客户端等待时间
从请求示意图我们可以发现,不管拆分请求,还是合并请求,请求其实都应该是并行处理的,都是以最耗时资源(Data C
)的获取为客户端的等待时间,所以相差不远。
PS:为什不考虑串行请求呢?因为这牺牲的是用户的等待时间,一般来说是不建议的
4.逻辑实现复杂度
对于拆分的情况来说,需要单独去发起请求、处理返回结果、建立处理重试机制等,所以会稍微复杂一点;相反,如果不对请求进行拆分的话,只需要处理一个请求,逻辑简单不少。
5.错误重试的成本
如果拆分了请求,在某个数据获取失败时,只需要重新获取该数据即可,而如果不进行拆分,中间某个环节出现错误的时候,需要再次获取之前已经获取过的数据,成本是比较高的。
6.服务端的吞吐率
从示意图可以看到,合并请求在服务器处理的时候,需要用到一个内部线程池
,而通常情况下,我们不会给这个内部线程池
配置很大的线程数(一般就是跟cpu核心数相关,低于100),这样会出现一个现象是:(假设tomcat
有2000条线程,内部线程池
大小设置为100)
- 在拆分请求的场景下,使用的资源是所有的
tomcat
线程,也就是2000个线程 - 在不拆分的场景下,请求会在
内部的线程池
中进行排队,实际有效的线程资源数是100个 因此,除非将内部线程池
的线程数设置得跟tomcat
线程池相当,否则直接压测得出的吞吐率意义不大。
五、总结
在日常的搬砖实践中,请求是否应当进行拆分的最大因素还是取决于业务的,没有绝对正确合适的判定条件,都是需要权衡各个方面的因素来做出决定。那么各位老板们,你们的请求拆分合理了吗?