关于 SpringBoot 中的并发请求外部接口的需求解惑

2024 年 11 月 19 日
 Koril

前言

老哥们,现在碰到一个需求,希望大家帮忙看看,有什么方案。 我是 Java 菜鸡,可能提到的某些点很傻很无知,望见谅。


需求

后台有这样一个接口 /demo ,前端请求到 /demo 后,代码需要按照顺序访问多个外部 HTTP 接口

比如外部接口有三个:

  1. /api-a
  2. /api-b
  3. /api-c

前端请求 /demo 后,后端直接返回 response 200 "ok" 就行,不用阻塞。

每个外部接口的一些传参都依赖于前一个请求的返回值(/api-c 的传参依赖于/api-b ),所以顺序是一定的,只能一个个请求。

最终结果(最后一个接口 /api-c 响应后),返回结果存入数据库。

另外,中间可能会出现 timeout 或者其他异常,这些信息也需要存入数据库。

问题在于,请求的并发量略微大了些,大概一秒钟有上百个请求进来(可以简化成每一秒就有 120 个请求进入该接口)。


机器环境、语言、框架、数据库

机器:单个虚拟机,CPU 和内存都可以按需求往上调大,目前是 18 核 48GB 的配置。

语言:Java 21 (抱歉,其他语言不会,只能用这个)

框架:SpringBoot3

数据库:PG 、Redis 、Mongo (随意使用)


我自己的方案

我的方法很直接,把请求的外部方法的代码放在一个 service 函数里,然后加 @Async 注解。

然后配置 ThreadPoolTaskExecutor (就是网上都能搜到的那些配置)。

另外,为了追踪每一个任务线程的结果,在线程里,一开始就生成一个 UUID ,然后构造一个对象,每一步都把相应的信息(成功或者失败)存入这个对象,最后以这个 UUID 为主键存储到数据库里。


有更好的解决方案么

按照我自己的观察,如果线程数量给小了,就容易产生队列堆积,给大了,又不确定该给多大,难道只能测试?

我的理解大概是 100 个请求进来,假设外部 3 个接口,每个需要 5 秒,那么全部请求完就是 15 秒(忽略其他时延),100 * 15 = 1500 个线程,如果小于这个值,就会堆积在队列中。

我想知道是否能根据以下的变量,通过某种方法推算出这个接口的理论的上限?

  1. 机器配置( CPU 个数,内存大小,上下行带宽等)
  2. 请求外部接口的个数,平均每个外部接口的响应时间
  3. 其他参数

怎么计算,并且达到这个上限?有什么更好的方法么?

5507 次点击
所在节点    程序员
56 条回复
Sezxy
2024 年 11 月 19 日
消息队列
qinxi
2024 年 11 月 19 日
换个思路, 用 MQ 去处理, 单独的服务去消费, 开几台消费者丰俭由人. 反正前端不用等结果. 不会影响接收请求的服务
cheng6563
2024 年 11 月 19 日
都 Java21 了,直接整虚拟线程了,都不用管线程数。
lucasdev
2024 年 11 月 19 日
"Java 21"? 要素察觉,虚拟线程
249239432
2024 年 11 月 19 日
数据要尽可能不丢失就消息队列
用 java 自带的队列也可以,简单方便
vZexc0m
2024 年 11 月 19 日
消息队列
dode
2024 年 11 月 19 日
使用多个长连接,循环请求这个接口,检查上传带宽占用多少
kd9yYw2RyhQwAwzn
2024 年 11 月 19 日
消息队列/线程池都行
lucasdev
2024 年 11 月 19 日
@lucasdev VirtualThreadTaskExecutor
spritecn
2024 年 11 月 19 日
随便怎么写,先加个监控布上去,有大量需求再调
TUNGH
2024 年 11 月 19 日
用消息队列也可以,用线程池也行,如果是线程池,你要确定并发数有多少,确定并发时间会持续的时长,三个外部接口,每个请求需要花多少时间,外部接口能不能接受你的高并发要求?你要根据以上几点来考虑如何设置线程池大小.
xubeiyou
2024 年 11 月 19 日
mq 合适 如果你希望维护这个 MQ 就是用 eventbus 谷歌的 你可以理解为一种不需要部署的内部 MQ 或者就是线程池起线程监听 处理
chen11
2024 年 11 月 19 日
用队列缓存前端进来的请求,后面开多少线程不是就随便搞
byte10
2024 年 11 月 19 日
NIO 可以的,比较清晰看到。当然虚拟线程也可以 ,但是要用对才行。
yangyuhan12138
2024 年 11 月 19 日
可以试试响应式编程,因为你的后一个 api 请求依赖前一个的结果,这个的吞吐量理论上也很高,关键词 Reactive
StoneHuLu
2024 年 11 月 19 日
直接起个 mq ,接口就直接推消息给 mq 就行了,然后写个消费程序,接受 mq 消息然后处理业务逻辑,如果程序报错就不要 ack ,走重试逻辑,如果请求数量很多并且是不间断地,处理不过来的话,就加消费程序就行了,这是标准处理方式
liaohongxing
2024 年 11 月 19 日
用虚拟线程最合适 ,虚拟线程依次串行访问 。还能大并发
zzz2Z
2024 年 11 月 19 日
mq 或者把任务存到数据库, 在通过定时任务/线程池拉取任务执行
flmn
2024 年 11 月 19 日
这种问题的标准解法是消息队列,如果没有 kafka/rabbit ,那么 redis 的 list 可以一用。
yikuo
2024 年 11 月 19 日
用 reactor 的异步 http 请求或者用虚拟线程

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1090698

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX