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

ars*_*nal 2 java performance multithreading executorservice resttemplate

我不是多线程专家,但我看到我当前使用的代码存在一些性能问题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<String> 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

class Task implements Callable<String> {

    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;

    }
}
Run Code Online (Sandbox Code Playgroud)

以下是我在另一个类代码DemoTest它调用getData的方法URLTest500 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();

    }
}   
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

Mar*_*nik 5

至于你的观察,你在外面测量15毫秒,但内部只测量3毫秒,我的赌注是建造带来RestTemplate差异.这可以通过重构来解决.

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


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

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