Eureka 服务通信组件RestTemplate + httpclient及http日志输出

4 篇文章 0 订阅
1 篇文章 0 订阅

这里写图片描述

在此使用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 <T>
     * @return
     */
    public <T>ResponseEntity<T> post(String url,Object reqBody,ParameterizedTypeReference<T> responseType){
        RestTemplate template = getRestTemplate(url);
        url = url.replace(directFlag, "");
        return template.exchange(url, HttpMethod.POST,new HttpEntity(reqBody),responseType);

    }

    public <T>ResponseEntity<T> get(String url,ParameterizedTypeReference<T> 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<String> response = rest.get(url, new ParameterizedTypeReference<RestResponse<String>>() {
        }).getBody();
        return response.getResult();
    }

}

具体地址稍后附链接.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值