RestTemplate(httpclient)动态设置超时时间

基本实现

httpclient版本

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.2</version>
</dependency>

RestTemplate构造

HttpClientBuilder httpClientBuilder = HttpClients.custom();
// HttpClient相关配置
HttpClient httpClient = httpClientBuilder.build();

// 使用自定义HttpClientRequestFactory
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpClientRequestFactory(httpClient);
clientHttpRequestFactory.setConnectTimeout(5000);
// 即为 SocketTimeout
clientHttpRequestFactory.setReadTimeout(30000);  
clientHttpRequestFactory.setConnectionRequestTimeout(5000);

RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory)

HttpClientRequestFactory类

import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.protocol.HttpContext;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;

import java.net.URI;

public class HttpClientRequestFactory extends HttpComponentsClientHttpRequestFactory {

    public static ThreadLocal<Integer> socketTimeoutThreadLocal = new ThreadLocal<>();

    public HttpClientRequestFactory(HttpClient httpClient) {
        super(httpClient);
    }

    @Override
    protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {
        HttpContext context = HttpClientContext.create();

        RequestConfig config = createRequestConfig(getHttpClient());

        // 从ThreadLocal中获取超时时间,并设置到context中
        Integer socketTimeout = socketTimeoutThreadLocal.get();
        if (null != socketTimeout) {
            RequestConfig.Builder builder = RequestConfig.copy(config);
            builder.setSocketTimeout(socketTimeout);
            config = builder.build();
        }

        context.setAttribute(HttpClientContext.REQUEST_CONFIG, config);
        return context;
    }
}

使用方法

// 设置当前请求超时时间
HttpClientRequestFactory.socketTimeoutThreadLocal.set(15000);
try {
    restTemplate.***()
} finally {
    // 清理ThreadLocal中超时时间
    HttpClientRequestFactory.socketTimeoutThreadLocal.remove();
}

原理解析

org.apache.http.impl.execchain.MainClientExec.execute(...)

httpclient在发起请求时会优先使用context中的超时时间设置

final RequestConfig config = context.getRequestConfig();
...
final int timeout = config.getSocketTimeout();
if (timeout >= 0) {
    managedConn.setSocketTimeout(timeout);
}

使用案例

import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.protocol.HttpContext;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;

import java.net.URI;

public class HttpClientRequestFactory extends HttpComponentsClientHttpRequestFactory {

    private static ThreadLocal<Integer> SOCKET_TIMEOUT_THREAD_LOCAL = new ThreadLocal<>();

    public HttpClientRequestFactory(HttpClient httpClient) {

        super(httpClient);
    }

    @Override
    protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {

        HttpContext context = HttpClientContext.create();
        RequestConfig config = createRequestConfig(getHttpClient());

        Integer socketTimeout = SOCKET_TIMEOUT_THREAD_LOCAL.get();
        if (null != socketTimeout) {
            RequestConfig.Builder builder = RequestConfig.copy(config);
            builder.setSocketTimeout(socketTimeout);
            config = builder.build();
        }

        context.setAttribute(HttpClientContext.REQUEST_CONFIG, config);
        return context;
    }

    @Override
    protected HttpUriRequest createHttpUriRequest(HttpMethod httpMethod, URI uri) {

        if (httpMethod == HttpMethod.GET) {
            return new HttpGetRequestForBody(uri);
        }
        return super.createHttpUriRequest(httpMethod, uri);
    }

    private static final class HttpGetRequestForBody extends HttpEntityEnclosingRequestBase {
        public HttpGetRequestForBody(final URI uri) {

            super.setURI(uri);
        }

        @Override
        public String getMethod() {

            return HttpMethod.GET.name();
        }
    }

    public static void set(Integer timeoutMills) {

        SOCKET_TIMEOUT_THREAD_LOCAL.set(timeoutMills);
    }

    public static void remove() {

        SOCKET_TIMEOUT_THREAD_LOCAL.remove();
    }
}
HttpClientRequestFactory.set(6000);
Result<ChannelsPayResponse> payCreateResult;
try {
    HttpEntity<ChannelsPayRequest> requestEntity = new HttpEntity<>(request, getHttpHeaders());
    ParameterizedTypeReference<Result<ChannelsPayResponse>> parameterizedTypeReference =
            new ParameterizedTypeReference<Result<ChannelsPayResponse>>() {};
    ResponseEntity<Result<ChannelsPayResponse>> responseEntity = restTemplate.exchange(String.format("http://%s/channels/pay", channelService.getServiceAppName()),
            HttpMethod.POST, requestEntity, parameterizedTypeReference);
    payCreateResult = responseEntity.getBody();
    log.info(JsonUtils.beanToJson(payCreateResult));
} catch (Exception e) {
    if (e.getCause() instanceof SocketTimeoutException) {
        Result<ChannelsPayQueryResponse> payQueryResult;
        try {
            HttpEntity<ChannelsPayQueryRequest> requestEntity = new HttpEntity<>(request, getHttpHeaders());
            ParameterizedTypeReference<Result<ChannelsPayQueryResponse>> parameterizedTypeReference =
                    new ParameterizedTypeReference<Result<ChannelsPayQueryResponse>>() {};
            ResponseEntity<Result<ChannelsPayQueryResponse>> responseEntity = restTemplate.exchange(String.format("http://%s/channels/pay/query", channelService.getServiceAppName()),
                    HttpMethod.POST, requestEntity, parameterizedTypeReference);
            payQueryResult = responseEntity.getBody();
            log.info(JsonUtils.beanToJson(payQueryResult));
        } catch (Exception e) {
            log.error("", e);
            throw e;
        }
    }
} finally {
    HttpClientRequestFactory.remove();
}

版权声明:
作者:Joe.Ye
链接:https://www.appblog.cn/index.php/2023/04/01/resttemplate-httpclient-dynamically-set-timeout-time/
来源:APP全栈技术分享
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
打赏
海报
RestTemplate(httpclient)动态设置超时时间
基本实现 httpclient版本 <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifac……
<<上一篇
下一篇>>
文章目录
关闭
目 录