请求重试,异常重试

需求:夜间定时任务比较多,偶尔遇到请求超时的情况,也可能是网络波动,总之需要做请求失败重试。

1,引入spring-retry注解

    <dependency>
        <groupId>org.springframework.retry</groupId>
        <artifactId>spring-retry</artifactId>
        <version>1.2.2.RELEASE</version>
    </dependency>

2,启动类开启重试注解扫描

@SpringBootApplication
@EnableRetry
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

3,业务类添加@Retryable

package com.example.demo.service;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.remoting.RemoteAccessException;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Random;

@Service
public class Testservice {

    @Retryable(value = {RemoteAccessException.class} ,backoff = @Backoff(delay = 3000l, multiplier = 0))
    public void main(){
            System.out.println("-----------发起请求");
//            String url = "http://127.0.0.1:8100/api/materials-product/updateRedis";
//            String res = requestByGetMethod(url);
//            JSONObject jo = JSONObject.parseObject(res);
//            int a = Integer.parseInt(jo.get("data").toString());

            //生成0~10之间的随机数
            Random r = new Random();
            int a = r.nextInt(5);   
            if (a > 2) {
                System.out.println("请求超时");
                throw new RemoteAccessException("请求超时或中断");
            } else {
                System.out.println("---------请求成功");
            }
    }


    @Recover()
    public void backroll(RemoteAccessException e){
        System.out.println("----->重试失败执行回调" + e.toString());
    }
    
    //真实远程连接
    public static String requestByGetMethod(String url) {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        StringBuilder entityStringBuilder = null;
        try {
            HttpPost get = new HttpPost(url);
            CloseableHttpResponse httpResponse = null;
            httpResponse = httpClient.execute(get);
            try {
                HttpEntity entity = httpResponse.getEntity();
                entityStringBuilder = new StringBuilder();
                if (null != entity) {
                    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent(), "UTF-8"), 8 * 1024);
                    String line = null;
                    while ((line = bufferedReader.readLine()) != null) {
                        entityStringBuilder.append(line);
                    }
                }
            } finally {
                httpResponse.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (httpClient != null) {
                    httpClient.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return entityStringBuilder.toString();
    }
}

4,运行测试

因为请求超时和网络中断情况不方便模拟,以上代码先通过随机数本地模拟请求失败的情况。 需要写一个简单的控制器不断发起请求。

@RestController
public class testController {
    @Autowired
    private Testservice testservice;
    
    @PostMapping("test")
    public String updateRedis() {
        testservice.main();
        return "已发送";
    }
}

4,运行测试

---------请求成功
-----------发起请求
---------请求成功
-----------发起请求
---------请求成功
-----------发起请求
请求超时
-----------发起请求
请求超时
-----------发起请求
请求超时
----->重试失败执行回调org.springframework.remoting.RemoteAccessException: 请求超时或中断

5,注解详解

1.@Retryable参数的说明
  • value:抛出指定异常才会重试
  • include:和value一样,默认为空,当exclude也为空时,默认所以异常
  • exclude:指定不处理的异常
  • maxAttempts:最大重试次数,默认3次
  • backoff:重试等待策略,默认使用@Backoff,@Backoff的value默认为1000L,我们设置为2000L;multiplier(指定延迟倍数)默认为0,表示固定暂停1秒后进行重试,如果把multiplier设置为1.5,则第一次重试为2秒,第二次为3秒,第三次为4.5秒。
2.@Recover注解

当重试耗尽时,RetryOperations可以将控制传递给另一个回调,用于@Retryable重试失败后处理方法,此方法里的异常一定要是@Retryable方法里抛出的异常,否则不会调用这个方法。

3.注意

使用了@Retryable的方法在本类中使用没有效果,只有在其他类中使用@Autowired注入或者@Bean才能生效。
 要触发@Recover方法,@Retryable方法不能存在返回值,只能是void
 使用@Retryable的方法里不能使用try catch包裹,要在方法上抛出异常,不然不会触发

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章