在我们正常的业务开发中,不免会发生请求第三方接口的应用场景,但由于网络不稳定的原因经常会发生一些问题,比如:请求虽然发出去,但返回的确实服务器繁忙、或者干脆没有返回信息等等,这时可以应用重试机制来解决这个问题,常用的重试有java Retry、springboot Retry与guava-retrying。这次主要介绍一下guava-retrying。guava-retrying是基于谷歌的核心类库guava的重试机制实现,本文一个常用的post与get的方法来介绍guava retrying重试机制的使用。
首先我们写一个http请求的工具类,其中有post与get方法
java
public abstract class HttpUtil {
public HttpUtil() {
}
protected JSONObject get(String requestUrl) throws IOException {
StringBuilder buffer = new StringBuilder();a
URL url = new URL(requestUrl);
HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
httpsURLConnection.setRequestProperty("accept", "*/*");
httpsURLConnection.setRequestProperty("connection", "Keep-Alive");
httpsURLConnection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
httpsURLConnection.setRequestMethod("GET");
httpsURLConnection.connect();
InputStream inputStream = httpsURLConnection.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str;
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
httpsURLConnection.disconnect();
return JSONObject.fromObject(buffer.toString());
}
protected JSONObject post(String requestUrl, String outputStr) throws Exception {
StringBuilder buffer = new StringBuilder();
TrustManager[] tm = new TrustManager[]{new MyX509TrustManager()};
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new SecureRandom());
SSLSocketFactory ssf = sslContext.getSocketFactory();
URL url = new URL(requestUrl);
HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
httpUrlConn.setSSLSocketFactory(ssf);
httpUrlConn.setRequestProperty("Content-type", "application/json");
httpUrlConn.setDoOutput(true);
httpUrlConn.setDoInput(true);
httpUrlConn.setUseCaches(false);
httpUrlConn.setReadTimeout(10000);
httpUrlConn.setConnectTimeout(10000);
httpUrlConn.setRequestMethod("POST");
if (null != outputStr) {
OutputStream outputStream = httpUrlConn.getOutputStream();
outputStream.write(outputStr.getBytes(Charset.forName("UTF-8")));
outputStream.close();
}
InputStream inputStream = httpUrlConn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str;
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
httpUrlConn.disconnect();
return JSONObject.fromObject(buffer.toString());
}
private static class MyX509TrustManager implements X509TrustManager {
private MyX509TrustManager() {
}
public void checkClientTrusted(X509Certificate[] chain, String authType) {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
}
接下来我们需要写一个类来继承HttpUtil工具类并重写其中的post方法与get方法
java
public class HttpService extends HttpUtil {
@Override
protected JSONObject post(String requestUrl, String outputStr) throws Exception {
return super.post(requestUrl, outputStr);
}
@Override
protected JSONObject get(String requestUrl) throws Exception{
return super.get(requestUrl);
}
}
增加重试机制的策略
java
private final Retryer<JSONObject> retryer = RetryerBuilder.<JSONObject>newBuilder()
.retryIfExceptionOfType(Exception.class) //根据异常重试
.retryIfException() //发生异常重试
.retryIfResult(Predicates.isNull()) //返回接口为null需要重试
.withWaitStrategy(WaitStrategies.fixedWait(3, TimeUnit.SECONDS)) //3毫秒后重试
.withStopStrategy(StopStrategies.stopAfterAttempt(3)) //重试次数
.withRetryListener(new MyRetryListener()) //注册监听 监听这里我们最后说
.build();
接下来将重试的机制配置到方法里,由于post与get的方法基本一致所以这里只列举post
java
@Override
public JSONObject get(String requestUrl) {
Callable<JSONObject> callable = () -> {
// TODO: 业务逻辑的实现
return super.get(requestUrl);
};
try {
return retryer.call(callable);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
java
/**
* @Author fallrain
* @Date 2021/3/10 15:12
* @description:重试失败监听器,监听可以使我们做好错误日志的整理,以便后期系统的优化
* @Version 1.0
*/
@Log4j2
public class MyRetryListener implements RetryListener {
@Override
public <V> void onRetry(Attempt<V> attempt) {
if (attempt.getAttemptNumber() == 1) return;
log.error("第{}次重试", attempt.getAttemptNumber());
log.error("重试原因:{}", attempt.getExceptionCause().toString());
if(attempt.getAttemptNumber()==3){
// TODO: 若第三次重试还是存在错误,则记录错误信息
}
}
}
java
//在使用时我们可以直接new一个出来使用
private HttpUtil httpUtil=new HttpService();
//也可以通过springboot IOC容器注入一个使用
@Autowired
public ServiceImpl(HttpUtil httpUtil) {
this.httpUtil = httpUtil;
}
java
//完整代码
@Service
public class HttpService extends HttpUtil {
public HttpService() {
}
private final Retryer<JSONObject> retryer = RetryerBuilder.<JSONObject>newBuilder()
.retryIfExceptionOfType(Exception.class) //根据异常重试
.retryIfException()
.retryIfResult(Predicates.isNull()) //返回接口为null需要重试
.withWaitStrategy(WaitStrategies.fixedWait(3, TimeUnit.SECONDS)) //3毫秒后重试
.withStopStrategy(StopStrategies.stopAfterAttempt(3)) //重试次数
.withRetryListener(new MyRetryListener()) //注册重试监听
.build();
@Override
public JSONObject post(String requestUrl, String outputStr) {
Callable<JSONObject> callable = () -> super.post(requestUrl, outputStr);
try {
return retryer.call(callable);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Override
public JSONObject get(String requestUrl) {
Callable<JSONObject> callable = () -> super.get(requestUrl);
try {
return retryer.call(callable);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}