RestTemplate使用

 在微服務都是以HTTP接口的形式暴露自身服務的,所以在調用遠程服務時就必須使用HTTP客戶端。咱們可使用JDK原生的URLConnection、Apache的Http Client、Netty的異步HTTP Client, Spring的RestTemplate。可是,用起來最方便、最優雅的仍是要屬Feign了。這裏介紹的是RestTemplate。html

什麼是RestTemplate?java

RestTemplate是Spring提供的用於訪問Rest服務的客戶端,web

RestTemplate提供了多種便捷訪問遠程Http服務的方法,可以大大提升客戶端的編寫效率。spring

調用RestTemplate的默認構造函數,RestTemplate對象在底層經過使用java.net包下的實現建立HTTP 請求,apache

能夠經過使用ClientHttpRequestFactory指定不一樣的HTTP請求方式。json

ClientHttpRequestFactory接口主要提供了兩種實現方式api

一、一種是SimpleClientHttpRequestFactory,使用J2SE提供的方式(既java.net包提供的方式)建立底層的Http請求鏈接。安全

二、一種方式是使用HttpComponentsClientHttpRequestFactory方式,底層使用HttpClient訪問遠程的Http服務,使用HttpClient能夠配置鏈接池和證書等信息。併發


xml配置的方式

請查看RestTemplate源碼瞭解細節,知其然知其因此然!app

RestTemplate默認是使用SimpleClientHttpRequestFactory,內部是調用jdk的HttpConnection,默認超時爲-1

@Autowired
RestTemplate simpleRestTemplate
@Autowired
RestTemplate restTemplate

 

 基於jdk的spring的RestTemplate

 

使用Httpclient鏈接池的方式

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
       default-autowire="byName" default-lazy-init="true">
 
    <!--方式2、使用httpclient的實現,帶鏈接池-->
    <bean id="ky.pollingConnectionManager" class="org.apache.http.impl.conn.PoolingHttpClientConnectionManager">
        <!--整個鏈接池的併發-->
        <property name="maxTotal" value="1000" />
        <!--每一個主機的併發-->
        <property name="defaultMaxPerRoute" value="1000" />
    </bean>
 
    <bean id="ky.httpClientBuilder" class="org.apache.http.impl.client.HttpClientBuilder" factory-method="create">
        <property name="connectionManager" ref="ky.pollingConnectionManager" />
        <!--開啓重試-->
        <property name="retryHandler">
            <bean class="org.apache.http.impl.client.DefaultHttpRequestRetryHandler">
                <constructor-arg value="2"/>
                <constructor-arg value="true"/>
            </bean>
        </property>
        <property name="defaultHeaders">
            <list>
                <bean class="org.apache.http.message.BasicHeader">
                    <constructor-arg value="User-Agent"/>
                    <constructor-arg value="Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.16 Safari/537.36"/>
                </bean>
                <bean class="org.apache.http.message.BasicHeader">
                    <constructor-arg value="Accept-Encoding"/>
                    <constructor-arg value="gzip,deflate"/>
                </bean>
                <bean class="org.apache.http.message.BasicHeader">
                    <constructor-arg value="Accept-Language"/>
                    <constructor-arg value="zh-CN"/>
                </bean>
            </list>
        </property>
    </bean>
 
    <bean id="ky.httpClient" factory-bean="ky.httpClientBuilder" factory-method="build" />
 
    <bean id="ky.clientHttpRequestFactory" class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
        <constructor-arg ref="ky.httpClient"/>
        <!--鏈接超時時間,毫秒-->
        <property name="connectTimeout" value="5000"/>
        <!--讀寫超時時間,毫秒-->
        <property name="readTimeout" value="10000"/>
    </bean>
 
    <bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
        <constructor-arg ref="ky.clientHttpRequestFactory"/>
        <property name="errorHandler">
            <bean class="org.springframework.web.client.DefaultResponseErrorHandler"/>
        </property>
        <property name="messageConverters">
            <list>
                <bean class="org.springframework.http.converter.FormHttpMessageConverter"/>
                <bean class="org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter"/>
                <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
                <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                    <property name="supportedMediaTypes">
                        <list>
                            <value>text/plain;charset=UTF-8</value>
                        </list>
                    </property>
                </bean>
            </list>
        </property>
    </bean>
 
</beans>

bean初始化+靜態工具

線程安全的單例(懶漢模式)

基於jdk的spring的RestTemplate

 

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
 
import javax.annotation.PostConstruct;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
 
/**
* @title:基於jdk的spring的RestTemplate
* @author:liuxing
* @date:2015-05-18 09:35
*/
@Component
@Lazy(false)
public class SimpleRestClient {
 
    private static final Logger LOGGER = LoggerFactory.getLogger(SimpleRestClient.class);
 
    private static RestTemplate restTemplate;
 
    static {
        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
        requestFactory.setReadTimeout(5000);
        requestFactory.setConnectTimeout(5000);
 
        // 添加轉換器
        List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
        messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
        messageConverters.add(new FormHttpMessageConverter());
        messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
        messageConverters.add(new MappingJackson2HttpMessageConverter());
 
        restTemplate = new RestTemplate(messageConverters);
        restTemplate.setRequestFactory(requestFactory);
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler());
 
        LOGGER.info("SimpleRestClient初始化完成");
    }
 
    private SimpleRestClient() {
 
    }
 
    @PostConstruct
    public static RestTemplate getClient() {
        return restTemplate;
    }
 
}

使用Httpclient鏈接池的方式

 

import org.apache.http.Header;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
 
import javax.annotation.PostConstruct;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
 
/**
* @title:使用spring的restTemplate替代httpclient工具
* @author:liuxing
* @date:2015-05-18 08:48
*/
@Component
@Lazy(false)
public class RestClient {
 
    private static final Logger LOGGER = LoggerFactory.getLogger(SimpleRestClient.class);
 
    private static RestTemplate restTemplate;
 
    static {
        // 長鏈接保持30秒
        PoolingHttpClientConnectionManager pollingConnectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS);
        // 總鏈接數
        pollingConnectionManager.setMaxTotal(1000);
        // 同路由的併發數
        pollingConnectionManager.setDefaultMaxPerRoute(1000);
 
        HttpClientBuilder httpClientBuilder = HttpClients.custom();
        httpClientBuilder.setConnectionManager(pollingConnectionManager);
        // 重試次數,默認是3次,沒有開啓
        httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(2, true));
        // 保持長鏈接配置,須要在頭添加Keep-Alive
        httpClientBuilder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy());
 
//        RequestConfig.Builder builder = RequestConfig.custom();
//        builder.setConnectionRequestTimeout(200);
//        builder.setConnectTimeout(5000);
//        builder.setSocketTimeout(5000);
//
//        RequestConfig requestConfig = builder.build();
//        httpClientBuilder.setDefaultRequestConfig(requestConfig);
 
        List<Header> headers = new ArrayList<>();
        headers.add(new BasicHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.16 Safari/537.36"));
        headers.add(new BasicHeader("Accept-Encoding", "gzip,deflate"));
        headers.add(new BasicHeader("Accept-Language", "zh-CN"));
        headers.add(new BasicHeader("Connection", "Keep-Alive"));
 
        httpClientBuilder.setDefaultHeaders(headers);
 
        HttpClient httpClient = httpClientBuilder.build();
 
        // httpClient鏈接配置,底層是配置RequestConfig
        HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
        // 鏈接超時
        clientHttpRequestFactory.setConnectTimeout(5000);
        // 數據讀取超時時間,即SocketTimeout
        clientHttpRequestFactory.setReadTimeout(5000);
        // 鏈接不夠用的等待時間,不宜過長,必須設置,好比鏈接不夠用時,時間過長將是災難性的
        clientHttpRequestFactory.setConnectionRequestTimeout(200);
        // 緩衝請求數據,默認值是true。經過POST或者PUT大量發送數據時,建議將此屬性更改成false,以避免耗盡內存。
        // clientHttpRequestFactory.setBufferRequestBody(false);
 
        // 添加內容轉換器
        List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
        messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
        messageConverters.add(new FormHttpMessageConverter());
        messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
        messageConverters.add(new MappingJackson2HttpMessageConverter());
 
        restTemplate = new RestTemplate(messageConverters);
        restTemplate.setRequestFactory(clientHttpRequestFactory);
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler());
 
        LOGGER.info("RestClient初始化完成");
    }
 
    private RestClient() {
 
    }
 
    @PostConstruct
    public static RestTemplate getClient() {
        return restTemplate;
    }
 
}

HttpClientUtils

 

import com.dooioo.commons.Strings;
import com.dooioo.framework.SpringContextHolder;
import com.dooioo.ky.cache.HttpClientResultCache;
import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
 
import java.util.Map;
 
/**
*
* 類功能說明:httpclient工具類,基於httpclient 4.x
* Title: HttpClientUtils.java
* @author 劉興
* @date 2014-3-7 下午7:48:58
* @version V1.0
*/
public class HttpClientUtils {
 
    private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientUtils.class);
 
    /**
     * post請求
     * @param url
     * @param formParams
     * @return
     */
    public static String doPost(String url, Map<String, String> formParams) {
        if (MapUtils.isEmpty(formParams)) {
            return doPost(url);
        }
 
        try {
            MultiValueMap<String, String> requestEntity = new LinkedMultiValueMap<>();
            formParams.keySet().stream().forEach(key -> requestEntity.add(key, MapUtils.getString(formParams, key, "")));
            return RestClient.getClient().postForObject(url, requestEntity, String.class);
        } catch (Exception e) {
            LOGGER.error("POST請求出錯:{}", url, e);
        }
 
        return Strings.EMPTY;
    }
 
    /**
     * post請求
     * @param url
     * @return
     */
    public static String doPost(String url) {
        try {
            return RestClient.getClient().postForObject(url, HttpEntity.EMPTY, String.class);
        } catch (Exception e) {
            LOGGER.error("POST請求出錯:{}", url, e);
        }
 
        return Strings.EMPTY;
    }
 
    /**
     * get請求
     * @param url
     * @return
     */
    public static String doGet(String url) {
        try {
            return RestClient.getClient().getForObject(url, String.class);
        } catch (Exception e) {
            LOGGER.error("GET請求出錯:{}", url, e);
        }
 
        return Strings.EMPTY;
    }
 
}

ErrorHolder

自定義的一個異常結果包裝類

使用樣例

api裏面能夠作自動的參數匹配:
如:http://you domainn name/test?empNo={empNo},則下面方法的最後一個參數爲數據匹配參數,會自動根據key進行查找,而後替換

API沒有聲明異常,注意進行異常處理

更多使用語法請查看API文檔

ResponseEntity<List<KyArea>> result = RestClient.getClient().exchange(DIVIDE_PLATE_API, HttpMethod.GET, HttpEntity.EMPTY, new ParameterizedTypeReference<List<KyArea>>() {},map("empNo", empNo));
List<KyArea> list = result.getBody();
 
ResponseEntity<KyArea> result = RestClient.getClient().exchange(DIVIDE_PLATE_API, HttpMethod.GET, HttpEntity.EMPTY, KyArea.class, map("empNo", empNo));
KyArea kyArea = result.getBody();

更多

RestTemplate API說明和使用參考

http://docs.spring.io/spring/docs/4.1.x/javadoc-api/org/springframework/web/client/RestTemplate.html

http://docs.spring.io/spring/docs/4.1.x/javadoc-api/org/springframework/http/client/SimpleClientHttpRequestFactory.html

http://docs.spring.io/spring/docs/4.1.x/javadoc-api/org/springframework/http/client/HttpComponentsClientHttpRequestFactory.html

HttpClient官方示例和參數配置說明

http://hc.apache.org/httpcomponents-client-4.4.x/examples.html

http://hc.apache.org/httpcomponents-client-4.4.x/tutorial/html/index.html

依賴
spring 3.x以上

注意點

1.關於httpclient配置的 defaultMaxPerRoute和 maxTotal
defaultMaxPerRoute:最大路由併發數,以主機爲單位
maxTotal:整個鏈接池的併發數

例如:
defaultMaxPerRoute爲10, maxTotal爲100
假設只會訪問http://www.baidu.com和http://www.google.com
那麼能同時併發到客源的只能是10,房源也是10,整個鏈接永遠不會到100

2.部分方法注意查看源碼,默認構造裏面會新增經常使用的數據轉換器,spring對jackson比較情有獨鍾,在解析xml和json時,優先使用jackson

相關文章
相關標籤/搜索