2

我在我的一个库中使用RestTemplate作为我的HttpClient。我不确定我是否在多线程环境中正确使用它,因为我的库将在多线程环境下在非常繁重的负载下使用,因此它必须非常快速。如何在多线程应用程序中有效使用RestTemplate?

下面是我的DataClient类:

public class DataClient implements Client { 

    private RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory()); 
    private ExecutorService executor = Executors.newFixedThreadPool(10); 

    // for synchronous call 
    @Override 
    public DataResponse executeSync(DataKey key) { 
     DataResponse dataResponse = null; 
     Future<DataResponse> future = null; 

     try { 
      future = executeAsync(key); 
      dataResponse = future.get(key.getTimeout(), TimeUnit.MILLISECONDS); 
     } catch (TimeoutException ex) { 
      dataResponse = new DataResponse(null, DataErrorEnum.TIMEOUT, DataStatusEnum.ERROR); 
      future.cancel(true); 
     } catch (Exception ex) { 
      dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR); 
     } 

     return dataResponse; 
    } 

    //for asynchronous call 
    @Override 
    public Future<DataResponse> executeAsync(DataKey key) { 
     Future<DataResponse> future = null; 
     Task task = new Task(key, restTemplate); 
     future = executor.submit(task); 

     return future; 
    } 

    // does this looks right? 
    private ClientHttpRequestFactory clientHttpRequestFactory() { 
     HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); 
     // setting 2000 ms as the default timeout for each Http Request 
     RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(2000).setConnectTimeout(2000) 
       .setSocketTimeout(2000).setStaleConnectionCheckEnabled(false).build(); 
     SocketConfig socketConfig = SocketConfig.custom().setSoKeepAlive(true).setTcpNoDelay(true).build(); 

     PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(); 
     poolingHttpClientConnectionManager.setMaxTotal(800); 
     poolingHttpClientConnectionManager.setDefaultMaxPerRoute(700); 

     CloseableHttpClient httpClientBuilder = HttpClientBuilder.create() 
       .setConnectionManager(poolingHttpClientConnectionManager).setDefaultRequestConfig(requestConfig) 
       .setDefaultSocketConfig(socketConfig).build(); 

     requestFactory.setHttpClient(httpClientBuilder); 
     return requestFactory; 
    } 
} 

将执行实际任务的简单类:

public class Task implements Callable<DataResponse> { 

    private final DataKey key; 
    private final RestTemplate restTemplate; 

    public Task(DataKey key, RestTemplate restTemplate) { 
     this.key = key; 
     this.restTemplate = restTemplate; 
    } 

    @Override 
    public DataResponse call() { 
     DataResponse dataResponse = null; 
     String response = null; 

     try { 
      String url = createURL(); 
      response = restTemplate.getForObject(url, String.class); 

      dataResponse = new DataResponse(response, DataErrorEnum.OK, DataStatusEnum.SUCCESS); 
     } catch (RestClientException ex) { 
      dataResponse = new DataResponse(null, DataErrorEnum.SERVER_DOWN, DataStatusEnum.ERROR); 
     } catch (Exception ex) { 
      dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR); 
     } 

     return dataResponse; 
    } 
} 

而下面是我厂里面我是用创造DataClient的一个实例,其意味着,它也将有单个实例RestTemplate

public class DataClientFactory { 

    private DataClientFactory() {} 

    private static class ClientHolder { 
     private static final DataClient INSTANCE = new DataClient(); 
    } 

    public static Client getInstance() { 
     return ClientHolder.INSTANCE; 
    } 
} 

这是这样,我会打个电话来获取数据:

DataResponse response = DataClientFactory.getInstance().executeSync(dataKey); 

现在的问题是 - 我不知道我是否正确使用RestTemplateHttpComponentsClientHttpRequestFactory。我是否需要PoolingHttpClientConnectionManager以及RestTemplate

我的主要目标是在多线程环境中高效地使用RestTemplate。由于我的图书馆将在非常沉重的负荷下使用,因此它必须非常快速。由于负载很重,我看到很多TIME_WAIT连接,所以我添加了clientHttpRequestFactory()方法与RestTemplate一起使用。

回答

0

RestTemplate is thread safe in Spring。因此,您可能想要在应用程序中仅创建一个RestTemplate实例,并在多个线程中共享它。这当然假设你将为所有人使用相同的HTTP属性(如超时,设置活动等)。如果您需要更改连接属性,则可以在应用程序开始的RestTemplate对象上创建一个池,并使用它将RestTemplate实例注入到调用方类中。

+0

我已经在我的问题中创建了一个'RestTemplate'实例吗?我稍微更新了它,告诉我如何使用DataClient类。所以我为所有调用使用相同的HTTP属性。你能再看看我的问题吗? – john

+0

我看到你正在为同步调用创建一个新线程。基本上浪费了在不需要时创建新线程的成本。请看http://stackoverflow.com/a/29447961/2776345。 –

0

如果您在restTemplate上执行的所有请求都将通过执行程序ExecutorService executor = Executors.newFixedThreadPool(10);执行,那么您可以通过这种方式自己管理restTemplate连接池。 不需要其他连接管理器。

然而,如果您使用PoolingHttpClientConnectionManager以及所有必要的配置(超时,连接数等),则会更好。

在结果,你写的,你不需要你的固定的线程池的Executor了, 因为每次要求你做restTemplate将得到的(什么ü没有以上)少得多的代码:

final Future<CPoolEntry> future = this.pool.lease(..) 

最后,如果您需要异步调用,可能值得尝试http://hc.apache.org/httpcomponents-asyncclient-4.1.x/index.html

0

RestTemplate itself is thread-safe。不过,我注意到你的private RestTemplate restTemplate不是final。因此不清楚它是来自DataClient的构造函数的safely published。参考从未改变,因此您可以简单地将其更改为final以确保。幸运的是,在你的任何任务尝试使用它之前,参考will be safely published,因为ExecutorService作出这样的保证,所以我相信你的代码是线程安全的。

相关问题