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

时间:2019-02-24 01:36:35

标签: java spring multithreading http asynchronous

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

(我正在Spring Boot中工作,并且如果有效果,我的系统将使用RabbitMQ AMQP构建。)

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

HttpResponse<String> response = httpClient.send(request, BodyHandlers.ofString());

我正在通过HTTP与之通信的该服务器需要一些时间来回复...例如3-4秒。因此,我的执行线程在此期间一直处于阻塞状态,等待答复。这样的扩展性很差,我的单线程没有执行,只是在等待回复到达-这很繁重。

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

换句话说,我想发送带有2-3个可用线程的数千个HTTP请求,并在响应到达时对其进行处理;我不想在执行每个请求之间造成任何重大延迟。

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

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

1 个答案:

答案 0 :(得分:4)

  

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

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

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

但是另一面是,同时发起太多HTTP请求可能会使目标服务和/或客户端或服务器端网络链接不堪重负。从这个角度来看,客户端限速可能是一件好事。


  

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

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

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


  

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

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

 nos_requests * average_request_time / nos_worker_threads

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

  

但是如果没有其他选择。

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

我认为真正的问题是服务器API设计要支持的功能与您要使用的功能之间不匹配。

肯定有问题:

  

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

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

没有神奇的尘埃尘埃会从同步HTTP客户端提供可扩展的性能。期间。