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 进一步提升程序性能,但是目前来讲已经够好了

posted @ 2016-05-31 16:10  SangS  阅读(28902)  评论(1编辑  收藏  举报