辽宁平台网站建设公司,心雨在线高端网站建设网页设计,直播间网站建设,wordpress数据库登陆目录
1、前言
2、实现方式
2.1、循环重试
2.2、递归重试
2.3、Spring Retry
2.4、Resilience4j
2.5、http请求网络工具内置重试方式
2.6、自定义重试工具
2.7、并发框架异步重试
2.8、消息队列
3、小结 1、前言
HTTP接口请求重试是指在请求失败时#xff0c;再次发… 目录
1、前言
2、实现方式
2.1、循环重试
2.2、递归重试
2.3、Spring Retry
2.4、Resilience4j
2.5、http请求网络工具内置重试方式
2.6、自定义重试工具
2.7、并发框架异步重试
2.8、消息队列
3、小结 1、前言
HTTP接口请求重试是指在请求失败时再次发起请求的机制。在实际应用中由于网络波动、服务器故障等原因HTTP接口请求可能会失败。为了保证系统的可用性和稳定性需要对HTTP接口请求进行重试。
2、实现方式
今天给大家分享一些常见的接口请求重试的方式。本地模拟了一个请求接口后面的代码示例均模拟请求该接口
GetMapping(http_test)
public String getHttpTest(){return 接口请求成功返回OK;
}
2.1、循环重试
循环重试是最简单最粗暴的方式就是在请求接口代码中加入循环机制如果接口请求失败则循环继续发起接口请求直到请求成功或接口重试次数达到上限。如果请求成功则不进行重试。
简单代码示例如下
GetMapping(retry_demo_loop)
public String retry_demo_loop(){// 重试上限次数为3次int maxRetryTime 3;String result null;// 接口循环请求for (int i 1; i maxRetryTime; i) {try {// 模拟请求接口result HttpUtil.get(http://localhost:8080/http_test);// 模拟一次请求失败if(i 1){int co i / 0;}// 请求成功跳出循环break;} catch (Exception e) {log.error(接口请求异常进行第{}次重试, i);result 接口请求失败请联系管理员;}}return result;
}
请求结果 重试日志打印 2.2、递归重试
除了循环还可以使用递归来实现接口的请求重试。递归是我们都比较熟悉的编程技巧在请求接口的方法中调用自身如果请求失败则继续调用直到请求成功或达到最大重试次数。
GetMapping(retry_demo_rec)
public String retry_demo_rec(){// 重试上限次数为3次int maxRetryTime 3;return retryRequest(maxRetryTime);
}/*** 递归方法* param maxRetryTime* return*/
private String retryRequest(int maxRetryTime){if (maxRetryTime 0) {return 接口请求失败请联系管理员;}int retryTime 0;try {// 模拟请求接口String result HttpUtil.get(http://localhost:8080/http_test);// 模拟一次请求失败if(maxRetryTime 3){int co 1 / 0;}return result;} catch (Exception e) {// 处理异常log.error(接口请求异常进行第{}次重试, retryTime);return retryRequest(maxRetryTime - 1);}
}
请求结果 重试日志打印 2.3、Spring Retry
第三种便是使用Spring Retry依赖实现。首先我们需要集成相关依赖
dependencygroupIdorg.springframework.retry/groupIdartifactIdspring-retry/artifactId
/dependency
!-- 由于retry使用到了aop所以还需要加入aop依赖 --
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-aop/artifactId
/dependency
加入EnableRetry启动
EnableRetry
SpringBootApplication
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}
添加retry方法注解
public interface MyRetryService {/*** retryable注解表示该方法需要重试* value出现该指定异常后进行重试* maxAttempts重试次数上限这里指定为3次* backoff重试策略这里指定200ms间隔一次* param code* return* throws Exception*/Retryable(value {Exception.class}, maxAttempts 3, backoff Backoff(200))String retry(int code) throws Exception;/*** 当重试达到上限后还是失败则作为异常回调方法* param th* param code* return*/RecoverString recover(Throwable th, int code);}
MyReretService实现类
Slf4j
Service
public class MyRetryServiceImpl implements MyRetryService {Overridepublic String retry(int code) throws Exception {log.info(请求retry接口);String result HttpUtil.get(http://localhost:8080/http_test);if(code ! 200){throw new Exception(接口请求异常);}return result;}Overridepublic String recover(Throwable th, int code) {log.error(回调方法执行);return 异常码为 code 异常信息: th.getMessage();}
}
Controller
Autowired
private MyRetryService myRetryService;/*** 当请求code参数为200时直接成功* 当code参数!200时会出发重试* param code* return* throws Exception*/
GetMapping(retry_demo_spring_retry)
public String retry_demo_spring_retry(Integer code) throws Exception {return myRetryService.retry(code);
}
访问地址http://localhost:8080/retry_demo_spring_retry?code123
查看结果可以看到接口重试了3次最后执行了Recover方法最后的回调。 2.4、Resilience4j
Resilience4j是一个轻量级、易于使用的轻量级“容错”包。它受Neflix Hystrix启发但只有一个依赖Vavr而不像Hystrix很多很多的依赖。同时它是一个 Java 库可以帮助我们构建弹性和容错的应用程序。Resilience4j在“容错”方面提供了各种模式断路器Circuit Breaker、重试Retry、限时器Time Limiter、限流器Rate Limiter、隔板BulkHead。我们今天讨论的话题是重试那么今天就来演示下Retry。
Github地址GitHub - resilience4j/resilience4j: Resilience4j is a fault tolerance library designed for Java8 and functional programming
首先添加相应依赖
dependencygroupIdio.github.resilience4j/groupIdartifactIdresilience4j-spring-boot2/artifactIdversion2.1.0/version
/dependency
application.yml配置相关策略配置官方文档https://resilience4j.readme.io/docs/retry
resilience4j:retry:instances:retry_demo:max-attempts: 3 # 重试的上限次数wait-duration: 1s # 重试的间隔时间配置为1s
我们改造一下上面spring-retry的demo。
controller
GetMapping(retry_demo_spring_retry)
Retry(name retry_demo, fallbackMethod recover)
public String retry_demo_spring_retry(Integer code) throws Exception {return myRetryService.retry(code);
}public String recover(Throwable th) {log.error(回调方法执行);return 异常信息 th.getMessage();
}
myRetryService
Override
public String retry(int code) throws Exception {log.info(请求retry接口);String result HttpUtil.get(http://localhost:8080/http_test);if(code ! 200){throw new Exception(接口请求异常);}return result;
}
程序执行打印结果 同样接口请求了3次均失败后执行了fallback回调方法。
2.5、http请求网络工具内置重试方式
通常一些外部的http网络工具都会内置一些重试的策略。如Apache HttpClient。这里以httpclient5为例。
首先添加依赖
dependencygroupIdorg.apache.httpcomponents.client5/groupIdartifactIdhttpclient5/artifactIdversion5.1.4/version
/dependency
定义HttpClient相关类指定重试策略。可以使用默认的DefaultHttpRequestRetryStrategy也可以自定义重试策略CustomRetryStrategy。
private static volatile CloseableHttpClient HTTP_CLIENT null;static {if(HTTP_CLIENT null){synchronized (HelloWorldController.class) {if(HTTP_CLIENT null){HTTP_CLIENT HttpClients.custom()// 设置重试策略
// .setRetryStrategy(new DefaultHttpRequestRetryStrategy(3, TimeValue.NEG_ONE_SECOND))// 自定义重试策略.setRetryStrategy(new CustomRetryStrategy()).build();}}}}
CustomRetryStrategy
public static class CustomRetryStrategy implements HttpRequestRetryStrategy {Overridepublic boolean retryRequest(HttpRequest httpRequest, IOException e, int executeCount, HttpContext httpContext) {return false;}Overridepublic boolean retryRequest(HttpResponse httpResponse, int executeCount, HttpContext httpContext) {System.out.println(进入重试策略);if(executeCount 3){System.out.println(重试超过3次终止重试);return false;}if(httpResponse.getCode() ! 200){System.out.println(http状态码不等于200进行重试);return true;}// 其他情况不重试return false;}Overridepublic TimeValue getRetryInterval(HttpResponse httpResponse, int executeCount, HttpContext httpContext) {return null;}
}
Controller代码
GetMapping(retry_demo_httpclient)
public String retry_demo_httpclient(Integer code) throws Exception {return httpclientRetry(code);
}private String httpclientRetry(int code) throws Exception {log.info(请求retry接口);// 这里模拟了一个不存在的地址HttpGet request new HttpGet(http://localhost:8080/http_test1);CloseableHttpResponse httpResponse HTTP_CLIENT.execute(request);String result IoUtil.read(httpResponse.getEntity().getContent()).toString();if(code ! 200){throw new Exception(接口请求异常);}return result;
}
访问接口地址http://localhost:8080/retry_demo_httpclient?code200。查看控制台日志打印 2.6、自定义重试工具
装X的话我们还可以自定义我们的重试工具。其实无非以下几个步骤
自定义重试的工具类接收一个方法调用并对该方法进行异常捕获如果捕获了该异常则进行一定间隔然后重新请求记录请求次数如果超过上限则提示异常信息
直接定义一个重试的工具类RetryUtil.java
import cn.hutool.core.thread.ThreadUtil;
import lombok.extern.slf4j.Slf4j;import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.Supplier;Slf4j
public class RetryUtil {/*** 重试方法* param invokeFunc 原方法调用* param maxAttempts 重试次数上限* param deplay 重试的间隔时间* param timeUnit 重试的间隔时间单位* param faultFunc 如果超过重试上限次数那么会执行该错误回调方法* return* param T*/public static T T retry(SupplierT invokeFunc, int maxAttempts, long deplay, TimeUnit timeUnit, FunctionThrowable, T faultFunc) {AtomicInteger retryTimes new AtomicInteger(0);for(;;) {try{return invokeFunc.get();} catch (Throwable th) {if(retryTimes.get() maxAttempts){log.error(重试次数超过{}次进入失败回调, retryTimes.get());return faultFunc.apply(th);}ThreadUtil.sleep(deplay, timeUnit);retryTimes.getAndAdd(1);}}}}
工具类使用
GetMapping(retry_demo_custom)
public String retry_demo_custom(Integer code) {return RetryUtil.retry(() - {String result null;try {result customRetry(code);} catch (Exception e) {throw new RuntimeException(e);}return result;}, 3, 1000, TimeUnit.MILLISECONDS, Throwable::getMessage);
}private String customRetry(int code) throws Exception {log.info(请求customRetry接口);String result HttpUtil.get(http://localhost:8080/http_test);if(code ! 200){throw new Exception(接口请求异常);}return result;
}
执行完后访问地址http://localhost:8080/retry_demo_custom?code2001 这里只是简单的进行了定义如果项目中使用肯定需要考虑更复杂的因素。如进入重试时不一定只有异常的时候需要重试可以指定重试策略然后制定进入重试策略的规则。
2.7、并发框架异步重试
在 Java 并发框架中异步重试通常涉及到使用线程池和定时器以便在异步任务失败后进行重试。以下是一个简单的示例演示了如何使用 CompletableFuture、ScheduledExecutorService 和 CompletableFuture.supplyAsync 来实现异步任务的重试。
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class AsyncRetryExample {private static final ScheduledExecutorService scheduler Executors.newScheduledThreadPool(1);public static void main(String[] args) {// 示例异步任务这里使用 supplyAsync你可以根据实际情况选择其他异步任务CompletableFutureString asyncTask CompletableFuture.supplyAsync(() - performAsyncTask(Task));// 异步任务失败后的重试逻辑retryAsyncTask(asyncTask, 3, 1, TimeUnit.SECONDS);}private static CompletableFutureString performAsyncTask(String taskName) {// 模拟异步任务这里可以是任何异步操作System.out.println(Performing async task: taskName);// 这里模拟任务失败的情况throw new RuntimeException(Task failed);}private static T void retryAsyncTask(CompletableFutureT asyncTask, int maxRetries, long delay, TimeUnit timeUnit) {asyncTask.exceptionally(throwable - {// 异步任务失败后的处理逻辑System.out.println(Task failed: throwable.getMessage());// 重试逻辑if (maxRetries 0) {System.out.println(Retrying...);CompletableFutureT retryTask CompletableFuture.supplyAsync(() - performAsyncTask(Retry Task));// 递归调用进行重试retryAsyncTask(retryTask, maxRetries - 1, delay, timeUnit);} else {System.out.println(Max retries reached. Task failed.);}return null; // 必须返回 null否则会影响链式调用});}
}示例中performAsyncTask 模拟了一个异步任务如果任务失败它会抛出一个运行时异常。retryAsyncTask 方法用于处理异步任务的失败情况并进行重试。在重试时它使用 CompletableFuture.supplyAsync 创建一个新的异步任务模拟了重试的过程。请注意这只是一个简单的示例实际应用中可能需要更复杂的重试策略和错误处理逻辑。
2.8、消息队列
网上还有一种消息队列的方式来实现这里没过多的去研究过目前以上几种方式应该也是够用的了。这里直接贴出网上的部分代码使用 RabbitMQ 作为消息队列演示了请求重试的实现
首先添加依赖
dependenciesdependencygroupIdcom.rabbitmq/groupIdartifactIdamqp-client/artifactIdversion5.13.1/version/dependency
/dependencies然后创建一个发送者和接收者类
消息发送者Producer
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;import java.io.IOException;
import java.util.concurrent.TimeoutException;public class MessageProducer {private static final String QUEUE_NAME retry_queue;public static void main(String[] args) throws IOException, TimeoutException {ConnectionFactory factory new ConnectionFactory();factory.setHost(localhost);try (Connection connection factory.newConnection(); Channel channel connection.createChannel()) {channel.queueDeclare(QUEUE_NAME, false, false, false, null);// 模拟发送请求String request Your request data;// 将请求发送到队列channel.basicPublish(, QUEUE_NAME, null, request.getBytes());System.out.println( [x] Sent request );}}
}消息接收者Consumer
import com.rabbitmq.client.*;import java.io.IOException;
import java.util.concurrent.TimeoutException;public class MessageConsumer {private static final String QUEUE_NAME retry_queue;public static void main(String[] args) throws IOException, TimeoutException {ConnectionFactory factory new ConnectionFactory();factory.setHost(localhost);try (Connection connection factory.newConnection(); Channel channel connection.createChannel()) {channel.queueDeclare(QUEUE_NAME, false, false, false, null);// 设置消息监听器DeliverCallback deliverCallback (consumerTag, delivery) - {String request new String(delivery.getBody(), UTF-8);// 模拟处理请求这里可能会出现处理失败的情况boolean processingSucceeded processRequest(request);if (processingSucceeded) {System.out.println( [x] Received and processed: request );} else {// 处理失败将请求重新放入队列进行重试channel.basicPublish(, QUEUE_NAME, null, delivery.getBody());System.out.println( [x] Processing failed. Retrying: request );}};// 消费消息channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag - {});}}private static boolean processRequest(String request) {// 模拟处理请求的方法// 在实际应用中这里应该是对请求的处理逻辑// 返回 true 表示处理成功返回 false 表示处理失败需要进行重试// 这里简单地模拟了一个失败的情况return !request.equals(Your request data);}
}示例中消息发送者MessageProducer将请求发送到名为 retry_queue 的队列中。消息接收者MessageConsumer监听队列当接收到消息时模拟处理请求的逻辑。如果处理失败将请求重新放入队列进行重试。
3、小结
接口请求重试机制对保证系统高可用非常关键,需要根据业务需求选择合适的重试策略。常用的组合策略包括带最大次数的定时/指数退避重试、故障转移重试等。重试机制需要综合设置以达到容错效果 又避免产生过大的系统负载。