java - 使用带有线程超时功能的ExecutorService如何提高性能?

标签 java multithreading performance executorservice resttemplate

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

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

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

我的客户端程序和实际服务器位于同一数据中心内,它们之间的 ping 时间延迟为 0.5 毫秒,因此它应该是可行的。

我使用 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;
    }
}

下面是我的任务类,它实现了 Callable 接口(interface) -

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;

    }
}

下面是我在另一个类DemoTest中的代码,它调用URLTest类中的getData方法500次并测量其端到端的第 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)直到任务完成..

我的主要目标是尽可能减少这里的延迟。我的用例很简单,对我的一台启用了超时功能的服务器进行 URL 调用,这意味着服务器是否需要花费大量时间来响应,然后使整个调用超时。客户将从那里的应用程序调用我们的代码,该应用程序也可以是多线程的。

有什么我遗漏的东西或者我需要使用一些其他风格的 ExecutorService 吗?我怎样才能提高我在这里的表现?任何建议都会有很大帮助..

任何示例将不胜感激..我正在阅读有关ExecutorCompletionService的内容,不确定我是否应该使用这个或其他东西..

最佳答案

根据您的观察,您在外部测量了 15 毫秒,但在内部仅测量了 3 毫秒,我敢打赌 RestTemplate 的构造会产生差异。这可以通过重构来解决。

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

<小时/>

如果您需要异步 HTTP 请求,那么您确实应该使用异步 HTTP 库,例如 AsyncHttpClient,它基于底层的 Netty,而 Netty 又基于 Java NIO。这意味着您不需要为每个未完成的 HTTP 请求占用一个线程。 AsyncHttpClient 还可以与 Futures 配合使用,因此您将拥有一个您习惯的 API。它还可以与回调一起使用,这是异步方法的首选。

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

关于java - 使用带有线程超时功能的ExecutorService如何提高性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21241036/

相关文章:

java - 我如何为执行程序选择线程数

performance - 优化插入列表中间

c# - 为什么C#画图形的速度比VB6慢

java - 从 main() 终止 Runnable() 对象

java - eclipse 有没有办法生成一个带有 getter 和 setter 的简单类

java - Excel - 自动输入到下一个空行

c# - Mvc RenderAction 性能与 RenderPartial

java - 当面板已满时将滚动面板添加到 jpanel (java)

c++ - 线程中指向this*的指针

javascript - 如何使用 Angular Fullstack 部署 Throng 或 Cluster?