在此使用HttpComponentsClientHttpRequestFactory方式实现,因为HttpComponentsClientHttpRequestFactory可以设置一些超时时间,连接数等信息.
首先需要创建HttpClientAutoConfiguration
package com.mooc.house.api.config;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.zalando.logbook.httpclient.LogbookHttpRequestInterceptor;
import org.zalando.logbook.httpclient.LogbookHttpResponseInterceptor;
@Configuration
@ConditionalOnClass({HttpClient.class})
@EnableConfigurationProperties(HttpClientProperties.class)
public class HttpClientAutoConfiguration {
// 打印日志拦截器
@Autowired
private LogbookHttpRequestInterceptor logbookHttpRequestInterceptor;
@Autowired
private LogbookHttpResponseInterceptor logbookHttpResponseInterceptor;
private final HttpClientProperties properties;
public HttpClientAutoConfiguration(HttpClientProperties properties){
this.properties = properties;
}
/**
* httpclient bean 的定义
* 有三种方式都可以让HttpClientAutoConfiguration这个自动配置生效
* 1.通过将自动配置所在package成为注解了@SpringBootApplication启动类的子package
* 2.通过定义META-INF/spring.factories文件,里面添加EnableAutoConfiguration与自动配置的映射关系
* 3.通过在启动类中添加注解EnableHttpClient,EnableHttpClient要@Import(HttpClientAutoConfiguration.class)
* @return
*/
@Bean
/**
* @ConditionalOnMissingBean:该注解表示,如果存在它修饰的类的bean,则不需要再创建这个bean;可以给该注解传入参数例如@ConditionOnMissingBean(name = "example"),这个表示如果name为“example”的bean存在,这该注解修饰的代码块不执行。
*/
@ConditionalOnMissingBean(HttpClient.class)
public HttpClient httpClient(){
//构建requestConfig
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(properties.getConnectTimeOut())//设置连接超时时间,默认1秒
.setSocketTimeout(properties.getSocketTimeOut()).build();//设置读超时时间,默认10秒
HttpClient client = HttpClientBuilder.create()
.setDefaultRequestConfig(requestConfig) //设置requestConfig
.setUserAgent(properties.getAgent())//设置User-Agent
.setMaxConnPerRoute(properties.getMaxConnPerRoute())//设置一个远端IP最大的连接数
.setMaxConnTotal(properties.getMaxConnTotaol())//设置总的连接数
.addInterceptorFirst(logbookHttpRequestInterceptor)
.addInterceptorFirst(logbookHttpResponseInterceptor)
// .setConnectionReuseStrategy(new NoConnectionReuseStrategy())//不重用连接,默认是重用,建议保持默认开启连接池,节省建立连接开销
.build();
return client;
}
}
.addInterceptorFirst(logbookHttpRequestInterceptor)
.addInterceptorFirst(logbookHttpResponseInterceptor)
这两行是为了记录打印的日志信息.具体使用请百度,很简单就是在application中配置,像这样
logbook.write.level=info
logbook.format.style=curl
创建上面Httpclient中引入的配置类HttpClientProperties
package com.mooc.house.api.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix="spring.httpclient")
public class HttpClientProperties {
private Integer connectTimeOut = 1000;
private Integer socketTimeOut = 10000;
private String agent = "agent";
private Integer maxConnPerRoute = 10;
private Integer maxConnTotaol = 50;
public Integer getConnectTimeOut() {
return connectTimeOut;
}
public void setConnectTimeOut(Integer connectTimeOut) {
this.connectTimeOut = connectTimeOut;
}
public Integer getSocketTimeOut() {
return socketTimeOut;
}
public void setSocketTimeOut(Integer socketTimeOut) {
this.socketTimeOut = socketTimeOut;
}
public String getAgent() {
return agent;
}
public void setAgent(String agent) {
this.agent = agent;
}
public Integer getMaxConnPerRoute() {
return maxConnPerRoute;
}
public void setMaxConnPerRoute(Integer maxConnPerRoute) {
this.maxConnPerRoute = maxConnPerRoute;
}
public Integer getMaxConnTotaol() {
return maxConnTotaol;
}
public void setMaxConnTotaol(Integer maxConnTotaol) {
this.maxConnTotaol = maxConnTotaol;
}
}
有需要单独配置的可以在application中使用spring.httpclient开头进行配置.
创建RestAutoConfig类.该类主要提供一个支持发送到eureka注册中心的RestTemplate 和一个直接通过ip:port方式访问的RestTemplate.
package com.mooc.house.api.config;/**
* Created by qx on 2018/8/25.
*/
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter4;
import org.apache.http.client.HttpClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.Charset;
import java.util.Arrays;
/**
*
*/
@Configuration
public class RestAutoConfig {
// 为了给配置分类
public static class RestTemplateConfig{
// 支持负载均衡的restTemplet
@Bean
@LoadBalanced//spring 对restTemplate bean进行定制,加入loadbalance拦截器进行ip:port的替换
RestTemplate lbRestTemplate(HttpClient httpclient) {
// 使RestTemplate底层实现,使用httpclient
RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory(httpclient));
// 因为rest是服务通信组件,需要发送请求个接收响应,在发送和接收请求的时候,都需要将对象序列化成一定的字符串或者字节数组,
// 如果发送包含中的字符串是需要编码的,但是默认的情况是,spring 默认编码成 ios-8859-1, 所以要设置成utf-8
template.getMessageConverters().add(0,new StringHttpMessageConverter(Charset.forName("utf-8")));
// 接收响应通过json反序列化出来
template.getMessageConverters().add(1,new FastJsonHttpMessageConvert5());
return template;
}
// 支持直连的restTemplet
@Bean
RestTemplate directRestTemplate(HttpClient httpclient) {
RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory(httpclient));
template.getMessageConverters().add(0,new StringHttpMessageConverter(Charset.forName("utf-8")));
template.getMessageConverters().add(1,new FastJsonHttpMessageConvert5());
return template;
}
public static class FastJsonHttpMessageConvert5 extends FastJsonHttpMessageConverter4 {
static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
public FastJsonHttpMessageConvert5(){
setDefaultCharset(DEFAULT_CHARSET);
//修改支持json的类型
setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON,new MediaType("application","*+json")));
}
}
}
}
注意:
FastJsonHttpMessageConverter4
默认使用的是
public FastJsonHttpMessageConverter4() {
super(MediaType.ALL);
}
MediaType.ALL方式处理中,spring使用字节流处理,而不是识别json.因此需要改造处理
“`
public static class FastJsonHttpMessageConvert5 extends FastJsonHttpMessageConverter4 {
static final Charset DEFAULT_CHARSET = Charset.forName(“UTF-8”);
public FastJsonHttpMessageConvert5(){
setDefaultCharset(DEFAULT_CHARSET);
//修改支持json的类型setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON,new MediaType("application","*+json")));
}
}
```
对RestAutoConfig进行二次封装,是业务方,不需要关心具体使用的哪一个restTemplet,只需要按照一定规则调用即可.
创建GenericRest
package com.mooc.house.api.config;/**
* Created by qx on 2018/8/25.
*/
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
/**
* 既支持直连又支持服务发现的调用
*
*/
@Service
public class GenericRest {
@Autowired
private RestTemplate lbRestTemplate;
@Autowired
private RestTemplate directRestTemplate;
// 判断是直连还是负载均衡restTemplet规则
private static final String directFlag = "direct://";
/**
*
* @param url
* @param reqBody
* @param responseType RestTemplate中使用ParameterizedTypeReference参数化类型支持泛型,主要是List
* https://www.jianshu.com/p/886922facc5f
* @param
* @return
*/
public ResponseEntity post(String url,Object reqBody,ParameterizedTypeReference responseType){
RestTemplate template = getRestTemplate(url);
url = url.replace(directFlag, "");
return template.exchange(url, HttpMethod.POST,new HttpEntity(reqBody),responseType);
}
public ResponseEntity get(String url,ParameterizedTypeReference responseType){
RestTemplate template = getRestTemplate(url);
url = url.replace(directFlag, "");
return template.exchange(url, HttpMethod.GET,HttpEntity.EMPTY,responseType);
}
private RestTemplate getRestTemplate(String url) {
if (url.contains(directFlag)){
return directRestTemplate;
}else {
return lbRestTemplate;
}
}
}
至此,封装的restTemplet就完成了,之后就可以愉快的使用这个这个调用了
@Repository
public class UserDao {
@Autowired
private GenericRest rest;
public String getUsername(Long id){
String url = "direct://http://127.0.0.1:8083/getUsername";
RestResponse response = rest.get(url, new ParameterizedTypeReference>() {
}).getBody();
return response.getResult();
}
}
具体地址稍后附链接.