封装复杂度之批量接口

简介: 封装复杂度之批量接口

一、背景

在平时项目开发过程中,难免需要作为接口提供方封装批量接口给上游调用;或者作为上游系统调用下游业务或者中间件的批量接口,执行某些操作。

lu.png

常见的批量操作有很多,比如批量查询内容详情,批量发送提醒;批量插入数据、批量更新、批量发送MQ消息等。


不知道,大家想过没有。


为什么要提供批量接口?


作为批量接口的提供方和批量接口的使用方我们通常需要注意哪些问题?


二、 问题思考

2.1 为什么要提供批量接口?

通常最主要的一个原因是为了性能优化。


通常 IO 操作是性能的主要瓶颈,批量接口可以减少网络 IO 次数,从而达到降低耗时的目的。


2.2 批量接口的提供方我们要注意哪些问题?

【1】健壮性

很多人,尤其是新手,容易直线思考,批量接口嘛,直接传给我一个 List 作为参数,返回结果即可。


(1) 批量限制


如果上游传入集合中元素多,会不会有问题?


上游传入的元素过多,很容易对本系统造成很多压力,而且非常容易超时。


public List queryOrders(String userId, List orderIds){

 // 省略

}

因此,批量接口通常需要增加分页参数,通常需要对集合长度进行检查。


public List queryOrders(String userId, List orderIds, PageRequest page){


 // orderIds size 检查

}

FBI Warning:请在函数的注释中或者接口文档中必须显式标注集合长度限制!


(2)参数校验


上游传入的参数合法性也要进行校验,比如例子中 userId 是否有权限查看这些 order ?


public List queryOrders(String userId, List orderIds, PageRequest page){

 // userId 合法性校验

 // orderIds size 检查


再比如传入日期,日期的格式是否正确,是否符合预期? 都是需要考量的事情。


(3)并发校验

有些批量操作不允许并发,要考虑加分布式锁。


(4)失败处理

失败该如何处理,也是一个需要考虑的问题


将失败的对象当做返回值返回给上游? 将失败的部分忽略掉?中间有数据失败,需要回滚?


【2】可拓展性

通常建议将主要参数甚至返回值定义成自定义对象,而不是使用封装类型在函数签名中铺开。


请看下面的案例,如果后续需要新增一些参数,就需要提供新的接口:


public List queryOrders(String userId, List orderIds, PageRequest page){

 // 省略

}

可以参考以下写法,将参数定义为批量查询对象:


public List queryOrders(OrderBatchQuery query, PageRequest page){

 // 省略

}

这样如果需要新增参数时,不需要修改函数签名。


对于一些“写”操作,还可以考虑,提供失败处理策略,如失败抛异常、部分失败返回失败列表等。


【3】封装复杂度

通常提供批量接口的同学会理直气壮的认为,设置集合 size 限制,最多再给个 page 参数就可以了。


如果有批量的需求,自己去对集合进行分批,自己对分页进行处理呗!


其实最大的问题是,几乎所有上游都需要对当前自己拿到的整个 list 的所有内容都要进行查询!!每个使用方都要自己处理分批和分页,非常麻烦,气得直跺脚!!!


其实有时候可以多走一步,既能体现出自己的专业度,也能更容易赢得上游的信任和称赞。


可以考虑提供一个自动分批和处理分页的方法(需告知上游虽然可以自动分批,但是如果 size 过大仍然会有因数据量过大导致调用超时,甚至 OOM 的风险)。


还可以提供一个自动对参数接口进行分批执行调用拼接结果的工具类等。


对于带返回值的调用,可以参考下面工具方法的定义:


public static  List partitionCall2ListAsync(List dataList,

                                                        int size,

                                                        ExecutorService executorService,

                                                    Function, List> function) {

 

}

                                                     

                                                 

其中 dataList 即待分批的集合, size 即每一批的数量, executorService 线程池,Function function 即单次调用。


可参考为的另外一篇博文:https://blog.csdn.net/w605283073/article/details/101399427


2.3 批量接口的使用方需要主要哪些问题?

【1】长度限制

不管是业务接口还是中间件的批量接口,通常参数中集合都会有 size 限制,一定仔细看函数说明、接口文档,甚至有条件拉下对方源码看看实现方式。


工作这几年,已经见到过身边同时多次因为使用下游提供的批量接口,而下游没有在接口上写 size 限制,导致上游在数据量大时报错,测试阶段通常数据量较小不容易发现该问题。


如果下游没有提供自动分批的批量调用方法,可以自己在本系统的外部依赖模块通过编写一个 XXX对应的 XXXXClient 进行二次封装,避免将复杂度再向上游暴露。


【2】 部分失败如何处理?

要核实下游对部分失败的情况的处理办法,是提供了失败策略,还是一起回滚,我们直接失败或者重试?


三、启发

本文希望通过批量接口的编写和使用,让大家意识到封装复杂性的必要性。


希望大家在设计方案或编写代码时,一定要带着“封装复杂度”的思想,尽量将复杂度封装在更底层的位置。


这也是“迪米特法则”即“最小知道原则”的要求,也是高内聚、低耦合的要求。


创作不易,如果你觉得本文对你有帮助,欢迎点赞、收藏加关注,你的支持和鼓励是我创作的最大动力

————————————————

版权声明:本文为CSDN博主「明明如月学长」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/w605283073/article/details/120575996

相关文章
|
3月前
|
缓存 前端开发 数据格式
构建前端防腐策略问题之保证组件层的代码不受到接口版本变化的问题如何解决
构建前端防腐策略问题之保证组件层的代码不受到接口版本变化的问题如何解决
|
程序员 C++
论接口的封装能力
论接口的封装能力
49 0
|
SQL 关系型数据库 MySQL
前后端交互接口性能速度优化
前后端交互接口性能速度优化
139 0
|
SQL 存储 缓存
18种接口实用优化方案总结
18种接口实用优化方案总结
207 0
|
存储 JSON 前端开发
Android数据库存储模块封装,让操作记录更好用可复用
Android数据库存储模块封装,让操作记录更好用可复用
|
XML JSON 前端开发
优化封装方案实现| 学习笔记
快速学习优化封装方案实现。
优化封装方案实现| 学习笔记
|
JSON 前端开发 决策智能
优化封装方案测试| 学习笔记
快速学习优化封装方案测试。
优化封装方案测试| 学习笔记
|
存储 安全 JavaScript
请求合并的 3 种方式,大大提高接口性能!
将相似或重复请求在上游系统中合并后发往下游系统,可以大大降低下游系统的负载,提升系统整体吞吐率。文章介绍了 hystrix collapser、ConcurrentHashMultiset、自实现BatchCollapser 三种请求合并技术,并通过其具体实现对比各自适用的场景。
|
JSON 前端开发 JavaScript
优化封装方案分析| 学习笔记
快速学习优化封装方案分析。
|
SQL 存储 缓存
哪些问题会引起接口性能问题
这个问题的答案非常多,需要根据自己的业务场景具体分析。
166 0
哪些问题会引起接口性能问题