差異處

這裏顯示兩個版本的差異處。

連向這個比對檢視

java:web:resteasy:invalid_connection_request_timeout [2018/06/02 19:01]
tony [Problem]
java:web:resteasy:invalid_connection_request_timeout [2023/06/25 09:48]
行 1: 行 1:
-{{tag>​resteasy httpclient}} 
-====== RESTEasy - Connection Pool的Request Timeout沒有作用 ====== 
-===== Problem ===== 
-REST API的開發者,大都會非常頻繁的存取REST服務提供者;Connection Pool是其中一個增進存取效率的方式。然而,這陣子在使用RESTEasy + Apache Connection Pool後,發現ConnectionRequestTimeout並沒作用。本篇文章主要分享目前的解決方法。 
-===== Version Info ===== 
-因為相依libraries的限制,我們目前還沒使用最新版本的RESTEasy,資訊如下:​ 
-<​code>​ 
-resteasy-client:​ 3.0.14.Final 
-httpcore: 4.3.3 
-httpclient: 4.3.6 
-</​code>​ 
-===== Test RESTClient ===== 
-我寫了一個測試去確認ConnectionRequestTimeout是否有作用,方法如下:​ 
-  - 將Pool大小設定為1。 
-  - 設定ConnectionRequestTimeout為2秒。 
-  - 依序發起兩個HTTP Get,但第一個請求不關閉。 
-  - 接著發起第二個HTTP Get,由於沒有有效的連線可以使用,會Block。 
-  - 在2秒後,Client會收到Exception,Root Cause為ConnectionPoolTimeoutException。 
-首先是HttpEngine設定的部分,由於這個版本ResteasyClientBuilder內建的PoolManager有些問題,因此我是另外產生的,程式碼大致如下:​ 
-<code bash> 
-// 這是自己寫的Builder,產生的實體是PoolingHttpClientConnectionManager 
-private HttpClientConnectionManager initConnectionManager(int poolSize, int perRoute) { 
- HttpClientConnectionManagerBuilder builder = new HttpClientConnectionManagerBuilder();​ 
- return builder.withMaxTotal(poolSize).withDefaultMaxPerRoute(perRoute).build();​ 
-} 
  
-// TestCase的timeout設定為20秒,避免有問題時會卡很久 
-@Test(timeout=20000) 
-public void testRequestTimeoutWithCustomHttpEngine() throws Exception { 
- int expectTimeout = 2*1000; 
- 
- connectionManager = initConnectionManager(20 , 1); 
-  
- RequestConfig requestConfig = RequestConfig.custom() 
- .setConnectionRequestTimeout(expectTimeout) 
- .build();​ 
-  
- ClientHttpEngine httpEngin = new ClientHttpEngineBuilder() 
- .withRequestConfig(requestConfig) 
- .withConnectionManager(connectionManager).build();​ 
-  
- ResteasyClientBuilder clientBuilder = (ResteasyClientBuilder)ResteasyClientBuilder.newBuilder();​ 
- clientBuilder.httpEngine(httpEngin);​ 
- // 以下略 
-</​code>​ 
-接著是發出兩個請求,第一個請求沒執行close,接著執行第二個請求:​ 
-<code java> 
- // 延續httpEngine的初始化 
- Client client = clientBuilder.build();​ 
-  
- Response leak_repsonse = client.target(target).request().get();​ 
- Response second_response = null; 
- 
- long before = System.currentTimeMillis();​ 
- try { 
- second_response = client.target(target).request().get();​ 
- fail("​should be timeout"​);​ 
- } catch (Exception e) { 
- assertNotNull(e.getCause());​ 
- assertTrue(e.getCause() instanceof ConnectionPoolTimeoutException);​ 
- assertEquals(expectTimeout,​ System.currentTimeMillis() - before, 500); 
- } finally { 
- leak_repsonse.close();​ 
- if( second_response != null ) 
- second_response.close();​ 
- } 
-} 
-</​code>​ 
-在執行以上測試後,會因為第二個請求block直到Test case 20秒timeout而錯誤;此測試並沒發生預期的ConnectionPoolTimeoutException。 
-===== Test HttpClient ===== 
-由於前一個測試沒達到預期效果,因此繼續確認PoolingHttpClientConnectionManager是否有問題。測試流程與前一個測試大同小異,差別只在於是直接對HttpClient做操作:​ 
-<code java> 
-@Test(timeout=20*1000) 
-public void testRequestTimeout() throws Exception { 
- int expect_timeout = 2*1000; 
- connectionManager = new HttpClientConnectionManagerBuilder().build();​ 
- RequestConfig requestConfig = RequestConfig.custom() 
- .setConnectionRequestTimeout(expect_timeout) 
- .build();​ 
-  
- HttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager) 
- .setDefaultRequestConfig(requestConfig) 
- .build();​ 
-  
- HttpUriRequest uriRequest = new HttpGet(target);​ 
- HttpResponse response = httpClient.execute(uriRequest);​ 
- assertEquals(200,​ response.getStatusLine().getStatusCode());​ 
- 
- long before = System.currentTimeMillis();​ 
- try { 
- uriRequest = new HttpGet(target);​ 
- response = httpClient.execute(uriRequest);​ 
- } catch( ConnectionPoolTimeoutException e ) { 
- assertEquals("​Timeout waiting for connection from pool", e.getMessage());​ 
- } 
- long duration = System.currentTimeMillis() - before; 
- assertEquals(expect_timeout,​ duration, 500); 
-} 
-</​code>​ 
-這個測試最後會因為第二個連線無法在2秒內取得而拋出ConnectionPoolTimeoutException。 
-===== How to fix? ===== 
-根據這兩個測試,懷疑是RESTEasy與HttpClient整合起來的問題。測試過更新RESTEasy與HttpClient的libraries後,確認只要將HttpClient的library升級至4.5.3以上就可以解決問題:​ 
-<​code>​ 
-resteasy-client:​ 3.0.14.Final 
-httpcore: 4.3.3 
-httpclient: 4.5.3 
-</​code>​ 
-=====    ===== 
----- 
-\\ 
-~~DISQUS~~