Spring基础篇(2)-重试

JAVA && Spring && SpringBoot2.x — 学习目录

每篇一问

当我们调用一个接口时,可能因为网络波动造成了系统之间超时,此时我们应该怎么办?

重试呗~
是的,对于读操作或者支持幂等的写操作,一般我们便可以进行重试操作。那如何进行重试?

不用担心,Spring都处理好了...


正常逻辑和重试机制耦合度较高。基于这个些问题,spring-retry规范了了正常逻辑和重试逻辑,将正常逻辑和重试逻辑解耦。spring-retry是一个开源工具包,该工具把重试操作模板定制化,可以设置重试策略和回退策略。同时,重试执行实例保证线程安全。spring-retry重试可以用Java代码实现也可以用注解@Retryable方式实现,这里spring-retry提倡以注解的方式对方法进行重试。

1、如何使用Spring-Retry进行重试

1. pom文件需要引入的依赖

  <!-- SpringRetry重试机制 -->
        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>

2. 启动类上配置自动加载

在SpringBoot 引入新技术,一般需要2步:
(1)pom 文件加入依赖;
(2)启动类上加上@EnableXXX注解;

  @SpringBootApplication
  @EnableRetry //开启retry重试机制
  public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class,args);
    }
}

3. @Retryable和@Recover的使用

@Service
public class RetryService {
    //开启日志控制
    private final static Logger logger = LoggerFactory.getLogger(RetryService.class);
    private final int totalNum = 100;

    /**
     * value:出现Exception.class进行重试,指定特定的异常
     * • include:和value一样,默认为空,当exclude也为空时,默认所以异常
     * • exclude:指定不处理的异常
     * •maxAttempts:最大重试次数,默认3次
     * •backoff:重试等待策略,默认使用@Backoff,@Backoff的value默认为1000L,
     * 我们设置为2000L;multiplier(指定延迟倍数)默认为0,表示固定暂停1秒后进行重试,
     * 如果把multiplier设置为1.5,则第一次重试为2秒,第二次为3秒,第三次为4.5
     *
     * @param num
     * @return
     */
    @Retryable(value = Exception.class, maxAttempts = 3,
            backoff = @Backoff(delay = 2000L, multiplier = 1.5))
    public int retry(int num) {
        logger.info("{}减库存开始", LocalTime.now());
        try {
            int i = 1 / 0;
        } catch (Exception e) {
            logger.error("illegal");
        }
        if (num <= 0) {
            throw new IllegalArgumentException("数量不对");
        }
        logger.info("减库存执行结束" + LocalTime.now());
        return totalNum - num;
    }


    /**
     * 作为降级方法调用的注释(重试调用完依旧失败,触发降级)。
     * 参数(可选):合适的Throwable类型的第一个参数(或Throwable的子类型),若是参数为空,只有其他降级注释标注的方法均不复合时,才会执行改方法。(后续可以携带请求参数,但是Throwable必须为第一个参数)
     *  此时该请求参数明确
     * 返回值:@Retryable 方法类型相同的返回值。
     */
    @Recover
    public int recover(IllegalArgumentException e,int num) {
        log.warn("recover减库存失败!!!" + LocalTime.now());
        return totalNum;
    }

    /**
     * 作为降级方法调用的注释(重试调用完依旧失败,触发降级)。
     * 参数(可选):合适的Throwable类型的第一个参数(或Throwable的子类型),若是参数为空,只有其他降级注释标注的方法均不复合时,才会执行改方法。
     * 返回值:@Retryable 方法类型相同的返回值。
     */
    @Recover
    public int recover2() {
        log.warn("recover2减库存失败2222!!!" + LocalTime.now());
        return totalNum;
    }

4. 测试方法

@RunWith(SpringRunner.class)
@SpringBootTest
public class RetryServiceTest {
    @Autowired
    RetryService retryService;

    @Test
    public void testRetry() {
        int i = retryService.retry(-1);
        System.out.println("数据是: "+ i );
    }
}

5. 测试效果

image.png

@Retryable注解
被注解的方法发生异常时会重试
value:指定发生的异常进行重试
include:和value一样,默认空,当exclude也为空时,所有异常都重试
exclude:指定异常不重试,默认空,当include也为空时,所有异常都重试
maxAttemps:重试次数,默认3
backoff:重试补偿机制,默认没有。
exceptionExpression:spEL表达式,若配置include或exclude参数那么该配置不会生效,其作用等效于include或exclude。格式#{@bean.methodName(#root)}methodName的返回值为boolean类型。#root是异常类,即用户可以在代码中判断是否进行重试。

    @Retryable(maxAttempts = 2,
            backoff = @Backoff(delay = 2000L, multiplier = 1.5),
            exceptionExpression = "#{@retryService.xxx(#root)}")
    public int retry(int num) {
        log.info("{}减库存开始", LocalTime.now());
        if (num <= 0) {
            throw new RuntimeException("数量不对");
        }
        log.info("减库存执行结束" + LocalTime.now());
        return totalNum - num;
    }
    
    public Boolean xxx(Exception e) {
        //根据异常信息决定是否重试!!!
        log.info("判断是否异常");
        return true;
    }

@Backoff注解
delay:指定延迟后重试
multiplier:指定延迟的倍数,比如delay=5000l,multiplier=2时,第一次重试为5秒后,第二次为10秒,第三次为20秒
@Recover
当重试到达指定次数时,被注解的方法将被回调,可以在该方法中进行日志处理。需要注意的是发生的异常和入参类型一致时才会回调。


下面咱们讲一下注意事项:

  1. 对于非幂等的读操作,应该禁止使用重试机制;
  2. 使用了@Retryable的方法不能在本类被调用(不能被套嵌使用),不然重试机制不会生效。由于retry用到了aspect增强,所有会有aspect的坑,就是方法内部调用,会使aspect增强失效,那么retry当然也会失效。
  3. 注解方法的异常不能通过try-catch捕获,应该throw出去,被捕获后处理,本质上就是AOP思想的使用。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容