parent
dc5f716d9b
commit
9765bf5f81
@ -0,0 +1,49 @@ |
||||
package com.shkj.wcs.third; |
||||
|
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory; |
||||
import org.springframework.retry.support.RetryTemplate; |
||||
import org.springframework.http.client.ClientHttpRequestInterceptor; |
||||
import org.springframework.web.client.HttpServerErrorException; |
||||
import org.springframework.web.client.ResourceAccessException; |
||||
import org.springframework.web.client.RestTemplate; |
||||
|
||||
import java.io.IOException; |
||||
import java.net.ConnectException; |
||||
import java.net.SocketTimeoutException; |
||||
import java.util.Collections; |
||||
import java.util.concurrent.TimeoutException; |
||||
|
||||
@Configuration |
||||
public class RetryConfig { |
||||
|
||||
@Bean |
||||
public RestTemplate restTemplate() { |
||||
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); |
||||
factory.setConnectTimeout(5000); // 连接超时5秒
|
||||
factory.setReadTimeout(10000); // 读取超时10秒
|
||||
|
||||
RestTemplate restTemplate = new RestTemplate(factory); |
||||
restTemplate.setInterceptors(Collections.singletonList(retryInterceptor())); |
||||
return restTemplate; |
||||
} |
||||
|
||||
@Bean |
||||
public ClientHttpRequestInterceptor retryInterceptor() { |
||||
return (request, body, execution) -> { |
||||
RetryTemplate retryTemplate = RetryTemplate.builder() |
||||
.maxAttempts(3) |
||||
.fixedBackoff(10000) |
||||
// 覆盖所有可能的网络异常
|
||||
.retryOn(ConnectException.class) |
||||
.retryOn(HttpServerErrorException.class) // 5xx错误
|
||||
.retryOn(SocketTimeoutException.class) // 读写超时
|
||||
.retryOn(ResourceAccessException.class) // RestTemplate包装的通用网络异常
|
||||
.retryOn(IOException.class) // 底层IO异常
|
||||
.build(); |
||||
|
||||
return retryTemplate.execute(context -> execution.execute(request, body)); |
||||
}; |
||||
} |
||||
} |
||||
@ -0,0 +1,87 @@ |
||||
package com.shkj.wcs.third; |
||||
|
||||
import cn.hutool.http.HttpRequest; |
||||
import cn.hutool.http.HttpResponse; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import java.net.SocketTimeoutException; |
||||
import java.net.ConnectException; |
||||
import java.util.concurrent.TimeUnit; |
||||
import java.util.concurrent.TimeoutException; |
||||
import java.util.Map; |
||||
|
||||
@Slf4j |
||||
public class RetryableHttpUtil { |
||||
|
||||
/** |
||||
* 仅在超时时重试的POST请求(带请求头) |
||||
* @param url 请求地址 |
||||
* @param json 请求体JSON |
||||
* @param headers 请求头Map |
||||
* @param maxRetries 最大重试次数(仅针对超时) |
||||
* @param retryIntervalMs 重试间隔(毫秒) |
||||
*/ |
||||
public static String postWithTimeoutRetry(String url, String json, Map<String, String> headers, |
||||
int maxRetries, long retryIntervalMs) throws Exception { |
||||
Exception lastException = null; |
||||
|
||||
for (int i = 0; i < maxRetries; i++) { |
||||
try { |
||||
HttpRequest request = HttpRequest.post(url) |
||||
.body(json) |
||||
.timeout(10000); // 设置超时时间(单位毫秒)
|
||||
|
||||
// 添加请求头
|
||||
if (headers != null && !headers.isEmpty()) { |
||||
for (Map.Entry<String, String> entry : headers.entrySet()) { |
||||
request.header(entry.getKey(), entry.getValue()); |
||||
} |
||||
} |
||||
|
||||
HttpResponse response = request.execute(); |
||||
|
||||
// 非2xx状态码直接抛出(如HTTP 4xx/5xx)
|
||||
if (!response.isOk()) { |
||||
throw new RuntimeException("HTTP " + response.getStatus() + ": " + response.body()); |
||||
} |
||||
return response.body(); |
||||
|
||||
} catch (Exception e) { |
||||
lastException = e; |
||||
|
||||
// 仅对以下超时相关异常重试
|
||||
boolean isTimeoutError = isTimeoutException(e); |
||||
|
||||
// 如果不是超时异常,或已达到最大重试次数,则抛出异常
|
||||
if (!isTimeoutError || i == maxRetries - 1) { |
||||
throw e; |
||||
} |
||||
|
||||
log.warn("超时异常 (尝试 {}/{}), {} 秒后重试. 错误: {}", |
||||
i + 1, maxRetries, retryIntervalMs / 1000, e.getMessage()); |
||||
|
||||
TimeUnit.MILLISECONDS.sleep(retryIntervalMs); |
||||
} |
||||
} |
||||
throw lastException != null ? lastException : new RuntimeException("未知错误"); |
||||
} |
||||
|
||||
/** |
||||
* 重载方法:不需要请求头的版本 |
||||
*/ |
||||
public static String postWithTimeoutRetry(String url, String json, |
||||
int maxRetries, long retryIntervalMs) throws Exception { |
||||
return postWithTimeoutRetry(url, json, null, maxRetries, retryIntervalMs); |
||||
} |
||||
|
||||
/** |
||||
* 判断是否为超时异常 |
||||
*/ |
||||
private static boolean isTimeoutException(Exception e) { |
||||
return e instanceof SocketTimeoutException // 读写超时
|
||||
|| e instanceof ConnectException // 连接超时
|
||||
|| e instanceof TimeoutException // 通用超时
|
||||
|| (e.getMessage() != null && |
||||
(e.getMessage().contains("timeout") || |
||||
e.getMessage().contains("timed out"))); |
||||
} |
||||
} |
||||
@ -0,0 +1,25 @@ |
||||
package com.shkj.wms.enums; |
||||
|
||||
|
||||
import lombok.AllArgsConstructor; |
||||
import lombok.Getter; |
||||
|
||||
@Getter |
||||
@AllArgsConstructor |
||||
public enum WmsErrorMessage { |
||||
|
||||
|
||||
/** |
||||
* agv任务未下发 |
||||
*/ |
||||
error1("1", "找不到设备任务"), |
||||
|
||||
/** |
||||
* wcs反馈失败 |
||||
*/ |
||||
error2("2", "容器在库或者存在未完成的设备任务") |
||||
; |
||||
|
||||
private String value; |
||||
private String desc; |
||||
} |
||||
Loading…
Reference in new issue