封装复杂度之批量接口

简介: 在平时项目开发过程中,难免需要作为接口提供方封装批量接口给上游调用;或者作为上游系统调用下游业务或者中间件的批量接口,执行某些操作。常见的批量操作有很多,比如批量查询内容详情,批量发送提醒;批量插入数据、批量更新、批量发送MQ消息等。不知道,大家想过没有。- 为什么要提供批量接口?- 作为批量接口的提供方和批量接口的使用方我们通常需要注意哪些问题?

一、背景

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

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

不知道,大家想过没有。

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

二、 问题思考

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

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

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

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

【1】健壮性

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

(1) 批量限制

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

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

public List<Order> queryOrders(String userId, List<String> orderIds){
  // 省略
}

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

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

  // orderIds size 检查
}

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

(2)参数校验

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

public List<Order> queryOrders(String userId, List<String> orderIds, PageRequest page){
  // userId 合法性校验
  // orderIds size 检查
}

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

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

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

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

【2】可拓展性

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

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

public List<Order> queryOrders(String userId, List<String> orderIds, PageRequest page){
  // 省略
}

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

public List<Order> queryOrders(OrderBatchQuery query, PageRequest page){
  // 省略
}

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

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

【3】封装复杂度

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

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

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

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

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

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

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

public static <T, V> List<V> partitionCall2ListAsync(List<T> dataList,
                                                         int size,
                                                         ExecutorService executorService,
                                                     Function<List<T>, List<V>> function) {
  
}
                                                         
                                                     

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

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

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

【1】长度限制

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

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

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

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

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

三、启发

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

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

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

创作不易,如果你觉得本文对你有帮助,欢迎点赞、收藏加关注,你的支持和鼓励是我创作的最大动力。
相关文章
|
存储 XML JSON
Activiti 7 核心数据库表概览及流程生命周期中的作用
Activiti 7 工作流引擎通过约25张核心数据库表实现流程定义、运行时状态、历史记录与身份数据的存储。表名以ACT_开头,后跟标识用途的字母组合(如RE表示Repository静态信息,RU表示Runtime动态数据)。流程启动时在运行时表登记数据,任务执行中更新关联信息,结束时清理运行时记录并完善历史记录。各表分工明确且逻辑紧密关联,确保高效运行与完整留痕的平衡。掌握这些表的作用和关联有助于深入理解Activiti底层原理及进行高级应用开发。
1045 0
|
存储 缓存 前端开发
Antd Upload + React-Cropper 实现图片自定义区域剪裁并上传功能
通过Upload组件结合react-Cropper实现图片的裁剪上传组件封装,剖析antd-img-crop源码实现的逻辑,对自己封装的组件进行进一步优化,改造!
5688 0
Antd Upload + React-Cropper 实现图片自定义区域剪裁并上传功能
|
Java Spring
动态控制 Spring Boot 中的 @Scheduled 定时任务
Spring Boot 中的 @Scheduled 注解为定时任务提供了一种很简单的实现,只需要在注解中加上一些属性,例如 fixedRate、fixedDelay、cron(最常用)等等,并且在启动类上面加上 @EnableScheduling 注解,就可以启动一个定时任务了。 但是在某些情况下,并没有这么简单,例如项目部署上线之后,我们可能会修改定时任务的执行时间,并且停止、重启定时任务等,因为定时任务是直接写死在程序中的,修改起来不是非常的方便。所以,简单记录一下自己的一些解决方案,仅供参考。
2822 0
|
机器学习/深度学习 数据采集 人工智能
人工智能安全(下)
人工智能安全(下)
1068 0
人工智能安全(下)
|
JavaScript 前端开发
JS如何配合input框实现模糊搜索
JS如何配合input框实现模糊搜索
576 2
|
11月前
|
运维 安全 数据中心
云专线对于企业有什么优势?
云专线是一种为企业提供高速、安全网络连接的解决方案,基于物理专线或虚拟专用网络技术,整合运营商骨干资源,构建总部、分支、数据中心与云端间的专属通道。其三层架构(接入、传输、管理)确保稳定低延迟传输与智能化运维。相比传统网络,云专线具备带宽独享、多重加密、灵活组网、流量感知等优势,适用于实时交互、数据备份、跨地域协同等场景。企业在选型时需综合考虑带宽、延迟、安全性需求及成本因素,选择合适的技术伙伴。云专线已成为企业数字化转型的关键基础设施,助力全球布局与业务创新。
323 0
|
存储 JavaScript
深入理解 Vuex 中的this.$store.dispatch方法
深入理解 Vuex 中的this.$store.dispatch方法
深入理解 Vuex 中的this.$store.dispatch方法
|
存储 监控 数据库
什么是聚集索引和非聚集索引?
【8月更文挑战第3天】
9875 6
|
存储 缓存 JSON
详解HTTP四种请求:POST、GET、DELETE、PUT
【4月更文挑战第3天】
75398 5
详解HTTP四种请求:POST、GET、DELETE、PUT