栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 前沿技术 > 大数据 > 大数据系统

RestTemplate详细配置及日志打印

RestTemplate详细配置及日志打印

目录
    • 1. RestTemplate配置类
    • 2. RestTemplateLog拦截器类
    • 3. 使用

1. RestTemplate配置类
  • 包括连接池,超时时间,拦截器,异常处理,字符集,response多次读取
  • 使用httpclient进行配置,引入依赖包
implementation group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.10'
package com.example.handlerinterceptor.config;

import com.example.handlerinterceptor.interceptor.RestTemplateLog;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HeaderElement;
import org.apache.http.HeaderElementIterator;
import org.apache.http.HttpHost;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.cookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.protocol.HTTP;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.BufferingClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Slf4j
@Configuration
public class RestTemplateConfig {

    
    private int connectionValidateAfterInactivityMs = 10 * 1000;

    
    private int maxTotalConnect = 50;

    
    private int maxConnectPerRoute = 10;

    
    private int connectTimeout = 20 * 1000;

    
    private int readTimeout = 20 * 1000;

    
    private int retryTimes = 2;

    
    private int connectionRequestTimout = 200;

    
    private static Map keepAliveTargetHost = Map.of("127.0.0.1", 20, "www.baidu.com", 30);

    
    private int keepAliveTime = 10;

    @Resource
    private RestTemplateLog restTemplateLog;

    
    @Bean
    public HttpClient httpClient(ConnectionKeepAliveStrategy connectionKeepAliveStrategy) {
        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
        // 使用Httpclient连接池的方式配置(推荐),同时支持netty,okHttp以及其他http框架
        PoolingHttpClientConnectionManager poolingHttpClientConnectionManager =
            new PoolingHttpClientConnectionManager();
        // 最大连接数
        poolingHttpClientConnectionManager.setMaxTotal(maxTotalConnect);
        // 同路由并发数
        poolingHttpClientConnectionManager.setDefaultMaxPerRoute(maxConnectPerRoute);
        // 官方推荐使用检查永久链接的可用性,而不推荐每次请求的时候才去检查 (milliseconds 毫秒)
        poolingHttpClientConnectionManager.setValidateAfterInactivity(connectionValidateAfterInactivityMs);

        // 配置连接池
        httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);

        // 重试次数
        httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(retryTimes, true));

        // 设置长连接保持策略
        httpClientBuilder.setKeepAliveStrategy(connectionKeepAliveStrategy);

        
        httpClientBuilder
            .setDefaultRequestConfig(RequestConfig.custom().setcookieSpec(cookieSpecs.IGNORE_cookieS).build());
        return httpClientBuilder.build();
    }

    
    @Bean
    public ClientHttpRequestFactory clientHttpRequestFactory(HttpClient httpClient) {
        HttpComponentsClientHttpRequestFactory clientHttpRequestFactory =
            new HttpComponentsClientHttpRequestFactory(httpClient);
        // 连接超时
        clientHttpRequestFactory.setConnectTimeout(connectTimeout);
        // 数据读取超时时间,即SocketTimeout
        clientHttpRequestFactory.setReadTimeout(readTimeout);
        // 从连接池获取请求连接的超时时间,不宜过长,必须设置,比如连接不够用时,时间过长将是灾难性的
        clientHttpRequestFactory.setConnectionRequestTimeout(connectionRequestTimout);
        return clientHttpRequestFactory;
    }

    
    @Bean
    public ConnectionKeepAliveStrategy connectionKeepAliveStrategy() {
        return (response, context) -> {
            // response的header中存在keep-alive字段使用timeout指定的时间
            HeaderElementIterator it = new BasicHeaderElementIterator(response.headerIterator(HTTP.CONN_KEEP_ALIVE));
            while (it.hasNext()) {
                HeaderElement he = it.nextElement();
                String param = he.getName();
                String value = he.getValue();
                if (value != null && "timeout".equalsIgnoreCase(param)) {
                    try {
                        return Long.parseLong(value) * 1000;
                    } catch (NumberFormatException ignore) {
                        log.error("解析长连接过期时间异常 {}", ignore);
                    }
                }
            }
            // response没有指定keep-alive时长,使用配置的时长,如果不在配置的ip,使用默认时长
            HttpHost target = (HttpHost)context.getAttribute(HttpClientContext.HTTP_TARGET_HOST);
            // 如果请求目标地址,单独配置了长连接保持时间,使用该配置
            Optional> any = Optional.ofNullable(keepAliveTargetHost).orElseGet(HashMap::new)
                .entrySet().stream().filter(e -> e.getKey().equalsIgnoreCase(target.getHostName())).findAny();
            // 否则使用默认长连接保持时间
            Long aLong = any.map(en -> en.getValue() * 1000L).orElse(keepAliveTime * 1000L);
            return aLong;
        };
    }

    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory clientHttpRequestFactory) {
        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
        // 解决401报错时,报java.net.HttpRetryException: cannot retry due to server authentication, in streaming mode
        requestFactory.setOutputStreaming(false);
        RestTemplate restTemplate = new RestTemplate(requestFactory);

        // 添加拦截器
        List interceptors = new ArrayList<>();
        interceptors.add(restTemplateLog);
        restTemplate.setInterceptors(interceptors);

        // 提供对传出/传入流的缓冲,可以让响应body多次读取(如果不配置,拦截器读取了Response流,再响应数据时会返回body=null)
        restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(clientHttpRequestFactory));

        // 我们采用RestTemplate内部的MessageConverter
        // 重新设置StringHttpMessageConverter字符集为UTF-8,解决中文乱码问题
        modifyDefaultCharset(restTemplate);

        // 请求失败异常处理,如果不重写hasError方法,抛出异常,无法执行后续代码
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
            @Override
            public boolean hasError(ClientHttpResponse response) {
                return false;
            }
        });
        return restTemplate;
    }

    private void modifyDefaultCharset(RestTemplate restTemplate) {
        List> converterList = restTemplate.getMessageConverters();
        HttpMessageConverter converterTarget = null;
        for (HttpMessageConverter item : converterList) {
            if (StringHttpMessageConverter.class == item.getClass()) {
                converterTarget = item;
                break;
            }
        }
        if (null != converterTarget) {
            converterList.remove(converterTarget);
        }
        Charset defaultCharset = StandardCharsets.UTF_8;
        converterList.add(1, new StringHttpMessageConverter(defaultCharset));
    }

}

2. RestTemplateLog拦截器类
  • 打印请求参数和返回结果
package com.example.handlerinterceptor.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Component
public class RestTemplateLog implements ClientHttpRequestInterceptor {

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
        throws IOException {
        traceRequest(request, body);
        ClientHttpResponse response = execution.execute(request, body);
        traceResponse(response);
        return response;
    }

    private void traceRequest(HttpRequest request, byte[] body) {
        Map map = new HashMap<>(16);
        map.put("requestUri", request.getURI().getPath());
        map.put("headers", String.valueOf(request.getHeaders()));
        map.put("parameter", new String(body, StandardCharsets.UTF_8));
        log.info("request: {}", map);
    }

    private void traceResponse(ClientHttpResponse response) throws IOException {
        StringBuilder inputStringBuilder = new StringBuilder();
        try (BufferedReader bufferedReader =
            new BufferedReader(new InputStreamReader(response.getBody(), StandardCharsets.UTF_8))) {
            String line = bufferedReader.readLine();
            while (line != null) {
                inputStringBuilder.append(line);
                inputStringBuilder.append('n');
                line = bufferedReader.readLine();
            }
        }
        Map map = new HashMap<>(16);
        map.put("httpCode", response.getStatusCode().value());
        map.put("responseBody", String.valueOf(inputStringBuilder));
        log.info("response: {}", map);
    }

}

3. 使用
package com.example.handlerinterceptor.sysuser.controller;

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.linkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@RestController
public class Test {
    @Resource
    private RestTemplate restTemplate;

    @GetMapping("/test")
    public void test() {
        MultiValueMap paramMap = new linkedMultiValueMap<>();
        paramMap.add("name", "Fisher3652");
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        HttpEntity> entity = new HttpEntity<>(paramMap, headers);
        ResponseEntity responseEntity =
                restTemplate.postForEntity("http://127.0.0.1:8081/test", entity, String.class);
        HttpHeaders responseHeaders = responseEntity.getHeaders();
        System.out.println(responseEntity.getBody());
        System.out.println(responseHeaders);
    }

}

  • 打印结果
2021-12-08 16:31:19.985  INFO 6043 --- [-nio-80-exec-10] c.e.h.interceptor.RestTemplateLog        : request: {headers=[Accept:"text/plain, application/json, application*", Content-Type:"application/x-www-form-urlencoded;charset=UTF-8", Content-Length:"15"], parameter=name=Fisher3652, requestUri=/test}
2021-12-08 16:31:19.990  INFO 6043 --- [-nio-80-exec-10] c.e.h.interceptor.RestTemplateLog        : response: {responseBody={"code":0,"msg":"Success","data":"Hello Fisher3652"}
, httpCode=200}
{"code":0,"msg":"Success","data":"Hello Fisher3652"}
[Content-Type:"application/json", Transfer-Encoding:"chunked", Date:"Wed, 08 Dec 2021 08:31:19 GMT", Keep-Alive:"timeout=60", Connection:"keep-alive"]

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/650980.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号