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

73 天前
 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. 其他参数

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

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

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

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

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

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

© 2021 V2EX