本文介绍了如何在使用具有线程超时功能的ExecutorService时提高性能?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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



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



我的客户端程序和实际服务器在同一数据中心内,并且ping时间延迟 0.5 ms 他们之间所以应该可以肯定..



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



以下是我为我编写的代码,它使用 ExecutorService Callables -

  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(完成!);
} catch(TimeoutException e){
System.out.println(Terminated!);
} catch(InterruptedException e){
e.printStackTrace();
} catch(ExecutionException e){
e.printStackTrace();
}

返回回复;
}
}

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

  class Task实现Callable< String> {

private RestTemplate restTemplate = new RestTemplate();

public String call()抛出异常{
// TimerTest timer = TimerTest.getInstance(); //第3行
String response = restTemplate.getForObject(url,String.class);
// timer.getDuration(); //第4行

返回响应;

}
}

以下是我在另一个的代码class DemoTest URLTest class getData 方法code> 500次并衡量其结束的第95个百分点 -

  public class DemoTest {
public static void main(String [] args){

URLTest bc = new URLTest();

//一点点热身
for(int i = 0; i< = 500; i ++){
bc.getData();
}

for(int i = 0; i< = 500; i ++){
TimerTest timer = TimerTest.getInstance(); //第1行
bc.getData();
timer.getDuration(); //第2行
}

//此方法打印出第95百分位数
logPercentileInfo();

}
}

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

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



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



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



任何例子都会非常感激..我正在阅读 ExecutorCompletionService 不知道我是否应该使用这个或其他东西..

解决方案

至于你在外面测量15毫秒的观察结果,但内部只有3毫秒,我的赌注是 RestTemplate 的构造取得了差异。这可以通过重构来修复。



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






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



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


I am not a Multithreading Expert but I am seeing some performance issues with my current code which is using ExecutorService.

I am working on a project in which I need to make a HTTP URL call to my server and if it is taking too long time to respond then timeout the call. Currently it is returning simple JSON String back..

Current requirement I have is for 10 ms. Within 10 ms it should be able to get the data back from the server. I guess its possible since it is just an HTTP call to server within the same datacenter.

My client program and actual servers are within same datacenter and ping time latency is 0.5 ms between them so it should be doable for sure..

I am using RestTemplate for this to make the URL call.

Below is my code which I have wrote for me which uses ExecutorService and Callables -

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;
    }
}

Below is my Task class which implements 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;

    }
}

And below is my code in another class DemoTest which calls the getData method in URLTest class 500 times and measure the 95th percentile of it end to end -

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();

    }
}

With the above code as it is, I am always seeing 95th percentile as 14-15 ms (which is bad for my use case as it is end to end flow and that's what I need to measure).

I am surprised why? Is ExectuorFramework adding all the latency here?. May be Each task is submitted, and the submitting thread is waiting (via future.get) until the task is finished..

My main goal is to reduce the latency here as much as possible.. My use case is simple, Make a URL call to one of my server with a TIMEOUT feature enabled, meaning if the server is taking lot of time to response, then Timeout the whole call. Customer will call our code from there application which can be multithreaded as well..

Is there anything I am missing or some other flavors of ExecutorService I need to use? How can I improve my performance here? Any suggestions will be of great help..

Any example will be greatly appreciated.. I was reading about ExecutorCompletionService not sure whether I should use this or something else..

解决方案

As for your observation that you are measuring 15 ms on the outside, but only 3 ms on the inside, my bet is that the construction of the RestTemplate takes the difference. This could be fixed by refactoring.

Note that RestTemplate is a heavyweight, thread-safe object, and is designed to be deployed as an application-wide singleton. Your current code is in critical violation of this intent.


If you need asynchronous HTTP requests, you should really use an asynchronous HTTP library such an AsyncHttpClient, based on Netty underneath, which is again based on Java NIO. That means that you don't need to occupy a thread per an outstanding HTTP request. AsyncHttpClient also works with Futures so you'll have an API you are used to. It can also work with callbacks, which is preferred for the asynchronous approach.

However, even if you keep your current synchronous library, you should at the very least configure a timeout on the REST client instead of letting it run its course.

这篇关于如何在使用具有线程超时功能的ExecutorService时提高性能?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-28 06:50