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作出這樣的保證,所以我相信你的代碼是線程安全的。

相關問題