封装复杂度之批量接口

简介: 在平时项目开发过程中,难免需要作为接口提供方封装批量接口给上游调用;或者作为上游系统调用下游业务或者中间件的批量接口,执行某些操作。常见的批量操作有很多,比如批量查询内容详情,批量发送提醒;批量插入数据、批量更新、批量发送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】 部分失败如何处理?

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

三、启发

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

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

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

创作不易,如果你觉得本文对你有帮助,欢迎点赞、收藏加关注,你的支持和鼓励是我创作的最大动力。
相关文章
|
Java Spring
动态控制 Spring Boot 中的 @Scheduled 定时任务
Spring Boot 中的 @Scheduled 注解为定时任务提供了一种很简单的实现,只需要在注解中加上一些属性,例如 fixedRate、fixedDelay、cron(最常用)等等,并且在启动类上面加上 @EnableScheduling 注解,就可以启动一个定时任务了。 但是在某些情况下,并没有这么简单,例如项目部署上线之后,我们可能会修改定时任务的执行时间,并且停止、重启定时任务等,因为定时任务是直接写死在程序中的,修改起来不是非常的方便。所以,简单记录一下自己的一些解决方案,仅供参考。
2972 0
|
Java API 调度
Android系统 自定义开机广播,禁止后台服务,运行手动安装应用接收开机广播
Android系统 自定义开机广播,禁止后台服务,运行手动安装应用接收开机广播
1966 0
|
前端开发 Go C++
CSS命名规则规范整理
大家在写css的时候,经常会遇到关于命名的问题。页面上成百甚至上千的class或者id,我们就会越来越感到困扰。 所以,这样我们就很有必要整理自己的一套命名规范。
10583 100
CSS命名规则规范整理
|
SQL Java 关系型数据库
JDBC Connection详解:连接到数据库的关键
在Java中,要与数据库进行交互,需要使用Java数据库连接(JDBC)。JDBC允许您连接到不同类型的数据库,并执行SQL查询、插入、更新和删除操作。在JDBC中,连接数据库是一个重要的步骤,而Connection对象是实现这一目标的关键。本篇博客将详细解释Connection对象的作用、创建和使用方法,以及与数据库连接相关的注意事项。
908 0
如何在 Umi 中使用 Keep Alive
如何在 Umi 中使用 Keep Alive
5102 1
如何在 Umi 中使用 Keep Alive
|
机器学习/深度学习 数据采集 人工智能
人工智能安全(下)
人工智能安全(下)
1182 0
人工智能安全(下)
|
测试技术 Apache Linux
怎么做服务器压力测试?
项目做好了,能不能顺利上线? 一般来说我们需要做一下压力测试来判断。比如该项目上线后预计每天一百万的接口访问量,并且访问时段主要集中在早八点到晚八点,那么平均下来 RPS 大约是 22 次左右·····
14589 0
|
JavaScript 前端开发
JS如何配合input框实现模糊搜索
JS如何配合input框实现模糊搜索
631 2
|
Shell 开发工具 git
【Git】解决Untracked Files Prevent Checkout的问题
【Git】解决Untracked Files Prevent Checkout的问题
5289 0
|
SQL 关系型数据库 MySQL
MySQL如何排查和删除重复数据
该文章介绍了在MySQL中如何排查和删除重复数据的方法,包括通过组合字段生成唯一标识符以及使用子查询和聚合函数来定位并删除重复记录的具体步骤。
1425 2