使用少量 Java 线程处理大量传统同步/阻塞 HTTP 客户端请求?

Zer*_*uno -1 java spring multithreading asynchronous http

我正在使用 Java。另一位软件开发人员向我提供了他的执行同步 HTTP 调用的代码并负责维护它 - 他正在使用com.google.api.client.http. 更新他的代码以使用带有回调的异步 HTTP 客户端不是一个可用的选项,我无法联系开发人员对其进行更改。但我仍然希望将回调附加到 HTTP 请求的高效异步行为。

(我在 Spring Boot 中工作,如果有任何影响,我的系统是使用 RabbitMQ AMQP 构建的。)

简单的 HTTP GET(它实际上是一个 API 调用)执行如下:

HttpResponse<String> response = httpClient.send(request, BodyHandlers.ofString());
Run Code Online (Sandbox Code Playgroud)

我通过 HTTP 与之通信的这个服务器需要一些时间来回复......比如说 3-4 秒。所以我的执行线程在这段时间内被阻塞,等待回复。这扩展性很差,我的单线程不做只是等待回复到达 - 这非常繁重。

当然,如果我想同时发送更多 HTTP 请求,我可以添加执行此调用的线程数,即我可以以这种方式进行扩展,但这听起来效率不高或不正确。如果可能的话,在这种情况下,我真的希望获得比 1 个线程等待 1 个 HTTP 请求更好的比率。

换句话说,我想用 2-3 个可用线程发送数千个 HTTP 请求,并在响应到达后进行处理;我不想在每个请求的执行之间产生任何明显的延迟。

我想知道:我怎样才能实现更具可扩展性的解决方案?如何处理每个线程的数千个 HTTP 调用?我应该看什么,还是我别无选择而我要求的是不可能的?

编辑:我想这是表达我的问题的另一种方式。假设我现在有 1000 个请求要发送,每个请求将持续 3-4 秒,但只有 4-5 个可用的执行线程可以发送它们。我想同时发送它们,但这是不可能的;如果我设法在 0.5 秒或更短的时间内将它们全部发送并通过一些回调或类似方式处理它们的请求,我会认为这是一个很好的解决方案。但我无法切换到异步 HTTP 客户端库。

Ste*_*n C 6

使用异步 HTTP 客户端不是一个可用的选项- 我无法更改我的 HTTP 客户端库。

在这种情况下,我认为您在客户端遇到了不可扩展的同步行为。

我能想到的唯一解决方法是将您的请求作为ExecutorService有界线程池中的任务运行。这将限制使用的线程数……但也会限制同时进行的 HTTP 请求数。这正在用另一个问题替代一个扩展问题:您有效地限制了您的 HTTP 请求。

但另一方面是同时启动过多的 HTTP 请求可能会淹没目标服务和/或客户端或服务器端网络链接。从这个角度来看,客户端的限速可能是一个很好的事情。


假设我现在有 1000 个请求要发送,每个请求将持续 3-4 秒,但只有 4-5 个可用的执行线程可以发送它们。我想同时发送它们,但这是不可能的;如果我设法在 0.5 秒或更短的时间内将它们全部发送并通过一些回调或类似方式处理它们的请求,我会认为这是一个很好的解决方案。但我无法切换到异步 HTTP 客户端。

您将能够使用 N 个线程同时运行 > N 个请求的唯一方法是使用异步客户端。时期。

“......回调或类似的东西......”。这是您只能通过异步客户端获得的功能。(或者更准确地说,如果幕后有真正的异步客户端库,您只能通过回调获得真正的异步行为。)


因此,该解决方案类似于以惊人的方式发送 HTTP 请求,即在一个请求和另一个请求之间存在一些延迟,其中每个延迟都受可用线程数的限制?如果每个请求之间的延迟不显着,我可以认为这是可以接受的,但我假设每个线程执行的时间之间会有相当大的延迟,因为每个线程必须等待对方完成(3-4s) ? 在那种情况下,这不是我想要的。

根据我提出的解决方法,任何两个请求之间的延迟都难以量化。但是,如果您尝试同时提交大量请求并等待所有响应,则各个请求之间的延迟无关紧要。对于这种情况,相关度量是完成所有请求所花费的时间。假设没有其他东西提交给执行者,完成请求所花费的时间大约是:

 nos_requests * average_request_time / nos_worker_threads
Run Code Online (Sandbox Code Playgroud)

另一件要注意的事情是,如果您确实设法同时提交了大量请求,则每个请求 3-4 秒的服务器延迟可能会增加。服务器每秒只能处理一定数量的请求。如果超过该容量,请求将被延迟或丢弃。

但如果没有其他选择。

我想,您可以考虑更改您的服务器 API,以便您可以在单个 HTTP 请求中提交多个“请求”。

我认为这里的真正问题是服务器 API 旨在支持的内容与您尝试用它做的事情之间存在不匹配。

这肯定有问题:

另一位软件开发人员向我提供了他的执行同步 HTTP 调用的代码并负责维护它 - 他使用的是 com.google.api.client.http。更新他的代码以使用带有回调的异步 HTTP 客户端不是一个可用的选项,我无法联系开发人员对其进行更改。

也许您需要“咬紧牙关”并停止使用他的代码。弄清楚它在做什么,并用您自己的实现替换它。

没有神奇的小精灵可以从同步 HTTP 客户端提供可扩展的性能。时期。

  • 那么,你只需要忍受它。抱歉,没有解决方案。(这不是 Java 的错。在任何实现语言中你都会遇到同样的问题/障碍......考虑到你所说的限制。) (2认同)