RestTemplate 使用总结

场景:

认证服务器须要有个 http client 把前端发来的请求转发到 backend service, 而后把 backend service 的结果再返回给前端,服务器自己只作认证功能。前端

遇到的问题:

  • 长链接以保证高性能。RestTemplate 自己也是一个 wrapper 其底层默认是 SimpleClientHttpRequestFactory ,若是要保证长链接, HttpComponentsClientHttpRequestFactory 是个更好的选择,它不只能够控制可以创建的链接数还能细粒度的控制到某个 server 的链接数,很是方便。在默认状况下,RestTemplate 到某个 server 的最大链接数只有 2, 通常须要调的更高些,最好等于 server 的 CPU 个数json

  • 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 bodyapp

  • 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 等等。async

下面是个人用法

@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 进一步提高程序性能,可是目前来说已经够好了性能