一、背景
有一个功能,提供两个接口,一个是A服务查询列表某天的数据,一个是B服务查询列表中单个对象某天的数据。
需要实现的效果是,
(1)调用A服务得到今天的数据
(2)然后再次调用A服务查询昨天的数据,
(3)然后循环A服务获取的数据依次调用B服务,查询每个对象的其他属性,然后获取某个中间结果。
(4)然后根据上面的中间结果和第依次调用A服务的结果组装最后结果(postLogic)。
整体的活动图,大致如下:
假如服务A调用时间为800ms,服务B调用getData的平均时间为300ms(假设10次), 则在执行postLogic前耗时约为800ms *2 + 300ms*10=4.6s。
二、方案
如果服务A的两次请求和服务B的一次请求,服务提供方可以包装成一次,当然效率会更高,但是无法提供。
那么,肿么办?
服务A的两次是可以异步并发请求的,而服务B依赖于服务A的第一次请求结果,因此如果服务A两次异步并发请求,则理想条件下耗时为800ms。
服务B的10次也可以异步并发请求,则服务器B的耗时理想状态下为300ms。
异步方案使用线程池执行Callable任务,返回值为Future对象。(带返回值的异步任务)
则postLogic之前总耗时被优化为800ms+300ms = 1.1s。
然后可以再优化,对结果进行缓存,如果缓存有数据直接返回,如果没有查询并计算后再缓存。
可以使用Redis,设置缓存失效时间。(典型的空间换时间)
这样不仅第一次请求耗时尽可能缩短,而且第二次以后请求超快(10-50ms)。
三、常见思路
我们要想想耗时的常见因素,主要是
- IO
- 网络
- 服务器性能
- 资源的创建和释放:线程的创建和销毁、连接(数据库连接、网络连接)的创建和销毁
- 转换:字符到字节转换等
- 算法的时间复杂度高(如多层for循环,而且数据量很大)
- 数据库查询条件复杂没命中索引等
因此我们思考的角度是
- 将串行变为并行或并发
- 同步操作变异步操作
- 多个请求合并成一个请求
- 用空间换时间
- 算法时间复杂度的优化
- 提高机器性能(CUP/内存/宽带/磁盘等)
- 利用各种池,如数据库连接池、缓存连接池等
- 数据库索引优化