RestTemplate 使用總結 轉

場景:

認證服務器需要有個 http client 把前端發來的請求轉發到 backend service, 然後把 backend service 的結果再返回給前端,服務器本身只做認證功能。

遇到的問題:

  • 長連接以保證高性能。RestTemplate 本身也是一個 wrapper 其底層默認是 SimpleClientHttpRequestFactory ,如果要保證長連接, HttpComponentsClientHttpRequestFactory 是個更好的選擇,它不僅可以控制能夠建立的連接數還能細粒度的控制到某個 server 的連接數,非常方便。在默認情況下,RestTemplate 到某個 server 的最大連接數只有 2, 一般需要調的更高些,最好等於 server 的 CPU 個數

  • access_token 不應傳到 backend service. backend service 之間通信不需要 token,因爲到這些服務的請求都是已經認證過的,是可信賴的用戶發出的請求。因此轉發請求時要把 parameter 從 request url 中刪掉。刪除 parameter 說難不難,說簡單其實還有點麻煩,網上有一個 UrlEncodedQueryString 可以參考下,它封裝了很多函數,其中就包括從url 中摘掉指定 header

  • 請求的 HttpMethod 問題。 HttpMethod 有很多種,http client 不應該對每種 Http method 都單獨處理,所以應選用 RestTemplate 的 exchange 方法。exchange 方法要求給出 RequestBody 參數,而對於 Get 請求,這部分往往爲空,所以我們要在 controller 中聲明 @RequestBody(required = false) String body

  • exchange 的返回值和 controller 的返回值。Restful API 一般都是返回 json 的,所以最簡單的是 exchange 和 controller 直接返回 String,但是返回 String 會有很多問題: 首先是如果某些 API 返回的是圖片,那麼這個 client 就傻掉了,需要爲圖片接口專門寫 API,此外如果 backend service 返回的是 Gzip,那麼此 client 必須對 gzip 先解壓縮再返回請求者,如果不解壓縮的話,相當於對着 gzip 數據做了到 String 類型的強制轉換,使得請求者拿到的數據無法解析,所以最好的返回值是 byte[]。對於那種比較大的 json 返回值,省去了對 String 的類型轉換後還能帶來很大的性能提升

  • 關於返回值是 byte[] 還是 ResponseEntity<byte[]> 的問題。我覺得還是 ResponseEntity<byte[]> 好些,因爲它就是 backend service 的結果。如果返回 byte[] 的話,還要對 HttpServletResponse 的 Header 進行修改,設置 Content-type, Content-encoding 等等。

下面是我的用法

    @PostConstruct
    public void setProperties() {
        clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(
                HttpClientBuilder.create()
                        .disableContentCompression()
                        .setMaxConnPerRoute(restTemplateConfig.getMaxConnPerRoute())
                        .setMaxConnTotal(restTemplateConfig.getMaxConn()).build());
        restTemplate = new RestTemplate(clientHttpRequestFactory);
    }


    public RestTemplate getInstance() {
        return new RestTemplate(new HttpComponentsClientHttpRequestFactory(HttpClientBuilder.create().build()));
    }

    public ResponseEntity<byte[]> mirrorRest(@RequestBody(required = false) String body, HttpMethod method,
                          HttpServletRequest request, HttpServletResponse response, URI uri) throws URISyntaxException {

        HttpEntity entities = new HttpEntity(body, extractHeaders(request));

        //delete accesstoken before mirror to backend server
        UrlEncodedQueryString queryString = UrlEncodedQueryString.parse(uri);
        queryString.remove("access_token");
        uri = queryString.apply(uri);

        ResponseEntity<byte[]> responseEntity = restTemplate.exchange(uri, method, entities, byte[].class);

        return responseEntity;
    }

需要改進的地方

  • HttpComponentsClientHttpRequestFactory 的配置粒度不夠細,可以配合 RequestConfig 確定某一個 service 需要多少連接數。

  • RestTemplate 有異步版本 asyncRestTemplate, 可以考慮用它結合 netty 進一步提升程序性能,但是目前來講已經夠好了

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