工作總結——RestTemplate請求時間過長問題

出現場景

項目使用微服務,將每個數據源拆分成了一個服務,並通過Eureka註冊,web服務通過配置的不同數據源的url調用各個數據源的服務從而獲取相應數據。

但近日部署後在跑全量更新緩存的過程中,發現了一個嚴重問題。緩存更新不完整,通過日誌信息定位到,每次在調用MongoDB數據源微服務時,會發生無響應,導致更新任務無法繼續進行下去,耗費大量時間。

而調用各個服務的接口正是使用RestTemplate實現的,但經過查看,RestTemplate的Bean並未進行超時配置。這就很方了,RestTemplate本身在不配置超時的時候,是不存在超時機制的,只能通過tcp協議的響應超時來結束連接,那要等多久?等不了的,系統等不了,我更等不了。

解決方案

嗯,反正問題是很好解決的,如下:

        HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
        httpRequestFactory.setConnectionRequestTimeout(10*1000);
        httpRequestFactory.setConnectTimeout(10*1000);
        httpRequestFactory.setReadTimeout(10*1000);
        return new RestTemplate(httpRequestFactory);

完事兒,在加載RestTemplate的bean的過程中,注入超時,具體的超時時間,是根據normal情況下系統跑出來的耗時時長進行的設置。

分析

藉此機會,準備好好分析下里面的配置項,從而達到,定義出一個合理的RestTemplate的Bean。

  • setConnectTimeout(int timeout)方法

設置基礎的HttpClient連接超時。

	public void setConnectTimeout(int timeout) {
		Assert.isTrue(timeout >= 0, "Timeout must be a non-negative value");
		this.requestConfig = requestConfigBuilder().setConnectTimeout(timeout).build();
		setLegacyConnectionTimeout(getHttpClient(), timeout);
	}
  • setBufferRequestBody(boolean bufferRequestBody)

該設置的默認值是ture,含義是使用內部緩存將請求的Body體進行緩衝處理。這裏呢,如果通過POST或PUT發送大量數據時,建議將此屬性更改爲false,以免內存不足。

	public void setBufferRequestBody(boolean bufferRequestBody) {
		this.bufferRequestBody = bufferRequestBody;
	}
  • setConnectionRequestTimeout(int connectionRequestTimeout)

設置使用基礎HttpClient從連接管理器請求連接時使用的超時(以毫秒爲單位)。可以看到,這裏的超時時間是允許爲0的,那麼問題來了,如果爲零會發生什麼事情:嗯沒錯,就是無限超時。

	public void setConnectionRequestTimeout(int connectionRequestTimeout) {
		this.requestConfig = requestConfigBuilder().setConnectionRequestTimeout(connectionRequestTimeout).build();
	}
  • setHttpClient(HttpClient httpClient)

哎喲,看到這個方法,我有一個大膽的想法。。。可以自實現一個自定義的HttpClient了。嗯嗯不錯

	public void setHttpClient(HttpClient httpClient) {
		this.httpClient = httpClient;
	}
  • setReadTimeout(int timeout)

該超時設置的是Socket讀取超時時間。

	public void setReadTimeout(int timeout) {
		Assert.isTrue(timeout >= 0, "Timeout must be a non-negative value");
		this.requestConfig = requestConfigBuilder().setSocketTimeout(timeout).build();
		setLegacySocketTimeout(getHttpClient(), timeout);
	}
  • setLegacyConnectionTimeout

將指定的連接超時應用於已棄用的HttpClient實現。

	private void setLegacyConnectionTimeout(HttpClient client, int timeout) {
		if (abstractHttpClientClass != null && abstractHttpClientClass.isInstance(client)) {
			client.getParams().setIntParameter(org.apache.http.params.CoreConnectionPNames.CONNECTION_TIMEOUT, timeout);
		}
	}
  • setLegacySocketTimeout

將指定的Socket超時應用於已棄用的HttpClient實現。

	private void setLegacySocketTimeout(HttpClient client, int timeout) {
		if (abstractHttpClientClass != null && abstractHttpClientClass.isInstance(client)) {
			client.getParams().setIntParameter(org.apache.http.params.CoreConnectionPNames.SO_TIMEOUT, timeout);
		}
	}

注意:這裏的已棄用的HttpClient的實現主要是指HttpClient 4.3版本之前的實現。

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章