springboot請(qǐng)求第三方接口時(shí)會(huì)用到RestTemplate,其底層實(shí)現(xiàn)邏輯默認(rèn)是通過(guò)SimpleClientHttpRequestFactory來(lái)實(shí)現(xiàn),具體由socket連接來(lái)實(shí)現(xiàn);可以替換其默認(rèn)實(shí)現(xiàn)為HttpComponentsClientHttpRequestFactory。
一、自定義RestTemplate實(shí)例對(duì)象
@Primary
@Bean
public RestTemplate restTemplate(ObjectProvider<HttpClientCustomizer> httpClientCustomizers, ClientHttpRequestFactory clientHttpRequestFactory, HttpClientProperties httpClientProperties) {
RestTemplate restTemplate = new RestTemplate();
//設(shè)置BufferingClientHttpRequestFactory將輸入流和輸出流保存到內(nèi)存中,允許多次讀取
restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(clientHttpRequestFactory));
//設(shè)置自定義異常處理
restTemplate.setErrorHandler(new CustomResponseErrorHandler());
if (httpClientProperties.isInterceptor()) {
//添加攔截器
restTemplate.setInterceptors(Collections.singletonList(httpClientCustomizers.orderedStream().findFirst().get()));
}
return restTemplate;
}
二、RestTemplate自定義全局超時(shí)時(shí)間
@Bean
public ClientHttpRequestFactory clientHttpRequestFactory(HttpClientProperties properties) throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
//SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
//讀取超時(shí)5秒,默認(rèn)無(wú)限限制,單位:毫秒
factory.setReadTimeout(properties.getReadTimeOut());
//連接超時(shí)10秒,默認(rèn)無(wú)限制,單位:毫秒
factory.setConnectTimeout(properties.getConnectTimeOut());
return factory;
}
三、RestTemplate設(shè)置單個(gè)請(qǐng)求的超時(shí)時(shí)間
首先看下HttpComponentsClientHttpRequestFactory類(lèi)的createRequest方法源碼:
@Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
HttpClient client = getHttpClient();
HttpUriRequest httpRequest = createHttpUriRequest(httpMethod, uri);
postProcessHttpRequest(httpRequest);
HttpContext context = createHttpContext(httpMethod, uri);
if (context == null) {
context = HttpClientContext.create();
}
// Request configuration not set in the context
if (context.getAttribute(HttpClientContext.REQUEST_CONFIG) == null) {
// Use request configuration given by the user, when available
RequestConfig config = null;
if (httpRequest instanceof Configurable) {
config = ((Configurable) httpRequest).getConfig();
}
if (config == null) {
config = createRequestConfig(client);
}
if (config != null) {
context.setAttribute(HttpClientContext.REQUEST_CONFIG, config);
}
}
if (this.bufferRequestBody) {
return new HttpComponentsClientHttpRequest(client, httpRequest, context);
}
else {
return new HttpComponentsStreamingClientHttpRequest(client, httpRequest, context);
}
}
其中createHttpContext方法默認(rèn)返回的是null,因此HttpContext中的RequestConfig配置值為null,所以需要按照接下來(lái)的代碼生成并設(shè)置;RequestConfig配置類(lèi)中的socketTimeout是設(shè)置讀取超時(shí)時(shí)間,connectTimeout是設(shè)置連接超時(shí)時(shí)間的兩個(gè)屬性,明白了這些就應(yīng)該知道怎樣設(shè)置單個(gè)請(qǐng)求超時(shí)時(shí)間了;
定義一個(gè)HttpContextFactory類(lèi),即HttpContext類(lèi)的一個(gè)實(shí)現(xiàn):
public class HttpContextFactory implements BiFunction<HttpMethod, URI, HttpContext> {
@Override
public HttpContext apply(HttpMethod httpMethod, URI uri) {
RequestConfig requestConfig = HttpContextHolder.peek();
if (Objects.nonNull(requestConfig)) {
HttpContext context = HttpClientContext.create();
context.setAttribute(HttpClientContext.REQUEST_CONFIG, requestConfig);
return context;
}
return null;
}
}
定義一個(gè)持有RequestConfig線程上下文對(duì)象類(lèi):
public class HttpContextHolder {
private static final ThreadLocal<RequestConfig> threadLocal = new NamedThreadLocal<>("HTTP進(jìn)程執(zhí)行狀態(tài)上下文");
public static void bind(RequestConfig requestConfig) {
threadLocal.set(requestConfig);
}
public static RequestConfig peek() {
return threadLocal.get();
}
public static void unbind() {
threadLocal.remove();
}
}
設(shè)置將HttpContextFactory類(lèi)實(shí)例對(duì)象:
@Bean
public ClientHttpRequestFactory clientHttpRequestFactory(HttpClientProperties properties) throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
//SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
//讀取超時(shí)5秒,默認(rèn)無(wú)限限制,單位:毫秒
factory.setReadTimeout(properties.getReadTimeOut());
//連接超時(shí)10秒,默認(rèn)無(wú)限制,單位:毫秒
factory.setConnectTimeout(properties.getConnectTimeOut());
//設(shè)置HTTP進(jìn)程執(zhí)行狀態(tài)工廠類(lèi)
factory.setHttpContextFactory(new HttpContextFactory());
return factory;
}
調(diào)用示例:
@RequestMapping("api/http")
@RestController
public class HttpClientController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("get1")
public BaseResponse get1(HttpServletRequest request) {
String timeout = request.getParameter("timeout");
BaseResponse<String> result;
try {
HttpContextHolder.bind(RequestConfig.custom().setSocketTimeout(2000).setConnectTimeout(-1).build());
result = restTemplate.getForObject("https://127.0.0.1:8080/api/http/testResponse?timeout=" + timeout, BaseResponse.class);
} finally {
HttpContextHolder.unbind();
}
return result;
}
}
這樣設(shè)置有點(diǎn)麻煩,可以定義一個(gè)注解,以AOP切面的方式來(lái)使用:
定義注解@TargetHttpTimeout:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface TargetHttpTimeout {
/**
* 讀取超時(shí)時(shí)間,默認(rèn):-1
*/
int readTimeout() default -1;
/**
* 連接超時(shí)時(shí)間,默認(rèn):-1
*/
int connectTimeout() default -1;
}
定義攔截器HttpTimeoutMethodInterceptor:
public class HttpTimeoutMethodInterceptor implements MethodInterceptor {
@Nullable
@Override
public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable {
try {
Method method = invocation.getMethod();
if (method.isAnnotationPresent(TargetHttpTimeout.class)) {
TargetHttpTimeout targetHttpTimeout = method.getAnnotation(TargetHttpTimeout.class);
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(targetHttpTimeout.readTimeout())
.setConnectTimeout(targetHttpTimeout.connectTimeout())
.build();
HttpContextHolder.bind(requestConfig);
}
return invocation.proceed();
} finally {
HttpContextHolder.unbind();
}
}
}
將AOP切面及切點(diǎn)關(guān)聯(lián)起來(lái)并注入容器:
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public Advisor httpTimeoutPointCutAdvice() {
//限定方法級(jí)別的切點(diǎn)
Pointcut mpc = new AnnotationMatchingPointcut(null, TargetHttpTimeout.class, false);
//組合切面(并集),即只要有一個(gè)切點(diǎn)的條件符合,則就攔截
Pointcut pointcut = new ComposablePointcut(mpc);
//切面增強(qiáng)類(lèi)
AnnotationPointcutAdvisor advisor = new AnnotationPointcutAdvisor(new HttpTimeoutMethodInterceptor(), pointcut);
//切面優(yōu)先級(jí)順序
advisor.setOrder(AopOrderInfo.HTTP_CLIENT_INTERCEPTOR);
return advisor;
}
通過(guò)上述幾步的優(yōu)化就可以優(yōu)雅的按照注解的方式設(shè)置單個(gè)請(qǐng)求的超時(shí)時(shí)間:
@GetMapping("get2")
@TargetHttpTimeout(readTimeout = 2000, connectTimeout = -1)
public BaseResponse get2(HttpServletRequest request) {
String timeout = request.getParameter("timeout");
BaseResponse<String> result = restTemplate.getForObject("https://127.0.0.1:8080/api/http/testResponse?timeout=1000", BaseResponse.class);
return result;
}
四、RestTemplate支持https請(qǐng)求
@Bean
public ClientHttpRequestFactory clientHttpRequestFactory(HttpClientProperties properties) throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
//SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
//讀取超時(shí)5秒,默認(rèn)無(wú)限限制,單位:毫秒
factory.setReadTimeout(properties.getReadTimeOut());
//連接超時(shí)10秒,默認(rèn)無(wú)限制,單位:毫秒
factory.setConnectTimeout(properties.getConnectTimeOut());
//設(shè)置HTTP進(jìn)程執(zhí)行狀態(tài)工廠類(lèi)
factory.setHttpContextFactory(new HttpContextFactory());
//開(kāi)啟HTTPS請(qǐng)求支持
if (properties.isSsl()) {
TrustStrategy acceptingTrustStrategy = (x509Certificates, authType) -> true;
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
SSLConnectionSocketFactory connectionSocketFactory = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(connectionSocketFactory).build();
factory.setHttpClient(httpClient);
}
return factory;
}
通過(guò)上述的步驟完整的實(shí)現(xiàn)了http全局超時(shí)時(shí)間的設(shè)置,單個(gè)請(qǐng)求超時(shí)時(shí)間設(shè)置,https請(qǐng)求支持;文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-407240.html
GitHub地址:https://github.com/mingyang66/spring-parent文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-407240.html
到了這里,關(guān)于解鎖新技能RestTemplate設(shè)置全局、單個(gè)請(qǐng)求超時(shí)時(shí)間及支持https請(qǐng)求的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!