如何在使用具有线程超时function的ExecutorService时提高性能?

我不是multithreading专家,但我发现当前使用ExecutorService代码存在一些性能问题。

我正在开发一个项目,在这个项目中我需要对我的服务器进行HTTP URL调用,如果响应时间过长则会超时。 目前它正在返回简单的JSON字符串..

我当前的要求是10 ms 。 在10 ms它应该能够从服务器获取数据。 我猜它是可能的,因为它只是对同一数据中心内的服务器的HTTP调用。

我的客户端程序和实际服务器在同一个数据中心内,并且它们之间的ping时间延迟为0.5 ms ,所以它应该是可行的。

我正在使用RestTemplate进行URL调用。

下面是我为我编写的使用ExecutorServiceCallables

 public class URLTest { private ExecutorService executor = Executors.newFixedThreadPool(10); public String getData() { Future future = executor.submit(new Task()); String response = null; try { System.out.println("Started.."); response = future.get(100, TimeUnit.MILLISECONDS); System.out.println("Finished!"); } catch (TimeoutException e) { System.out.println("Terminated!"); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } return response; } } 

下面是我的Task类,它实现了Callable接口 –

 class Task implements Callable { private RestTemplate restTemplate = new RestTemplate(); public String call() throws Exception { // TimerTest timer = TimerTest.getInstance(); // line 3 String response = restTemplate.getForObject(url, String.class); // timer.getDuration(); // line 4 return response; } } 

下面是我在另一个类DemoTest代码,它在URLTest类中调用getData方法500 times并测量其端到端的第95个百分点 –

 public class DemoTest { public static void main(String[] args) { URLTest bc = new URLTest(); // little bit warmup for (int i = 0; i <= 500; i++) { bc.getData(); } for (int i = 0; i <= 500; i++) { TimerTest timer = TimerTest.getInstance(); // line 1 bc.getData(); timer.getDuration(); // line 2 } // this method prints out the 95th percentile logPercentileInfo(); } } 

使用上面的代码,我总是看到第95百分位数为14-15 ms (这对我的用例来说是不好的,因为它是端到端流程,而这正是我需要测量的)。

我很惊讶为什么? ExectuorFramework在这里添加所有延迟吗? 可能是每个任务都已提交,并且提交线程正在等待(通过future.get),直到任务完成为止。

我的主要目标是尽可能减少延迟。我的用例很简单,在启用了TIMEOUTfunction的情况下对我的某个服务器进行URL调用,这意味着如果服务器花了很多时间来响应,那么Timeout整个电话。 客户将从那里可以multithreading的应用程序调用我们的代码。

有什么我缺少的或我需要使用的其他一些ExecutorService吗? 我怎样才能提高我的表现? 任何建议都会有很大的帮助..

任何例子都将非常感激..我正在阅读有关ExecutorCompletionService不确定我是否应该使用这个或其他东西..

至于你的观察,你在外面测量15毫秒,但内部只测量3毫秒,我的赌注是RestTemplate的构造有所不同。 这可以通过重构来解决。

请注意, RestTemplate是一个重量级,线程安全的对象,旨在部署为应用程序范围的单例 。 您当前的代码严重违反了此意图。


如果您需要异步HTTP请求,您应该使用异步HTTP库,例如基于下面的Netty的AsyncHttpClient ,它再次基于Java NIO。 这意味着您不需要为每个未完成的HTTP请求占用一个线程。 AsyncHttpClient也适用于Futures,因此您将拥有一个习惯的API。 它也可以与回调一起使用,这是异步方法的首选。

但是,即使您保留当前的同步库,也应该至少在REST客户端上配置超时,而不是让它运行。

然后再次运行该程序它将开始给我95%百分位为3毫秒。 所以不确定为什么端到端流量给我95%的百分位数为14-15毫秒

您正在以比处理它们更快的速度生成任务。 这意味着你运行测试的时间越长,它排队的时间就越远。 我希望如果你提出这2000个请求,你会看到延迟达到你现在所做的4倍。 瓶颈可能在客户端(在这种情况下,更multithreading会有所帮助),但很可能瓶颈在服务器端,在这种情况下,更多的线程可能会使它变得更糟。


HTTP的默认行为是为每个请求建立新的TCP连接。 即使您有两台并排的计算机,新TCP连接的连接时间也可以轻松达到20毫秒。 我建议使用HTTp / 1.1并保持持久连接。

顺便说一下你可以在0.5毫秒内从伦敦的一边ping到另一边。 但是,使用HTTP可靠地低于1毫秒是棘手的,因为协议不是为低延迟而设计的。 它专为在高延迟网络上使用而设计。

注意:您无法看到低于25毫秒的延迟,100毫秒足够快,足以满足大多数Web请求。 正是出于这种假设,HTTP才被设计出来。