MelonBlog

使用Spring Retry增加业务的健壮性

前言

我维护的一个项目里,经常会遇到一个错误,每一次业务场景都需要生成一个唯一的业务id,有一个专门生成业务id的序列化服务,业务服务会频繁调用这个序列化服务,因为序列化服务的代码并非我开发的,并且它依赖的组件版本有点旧,所以我得想办法解决这个问题,并且尽量在我自己维护的业务模块里做改动。

所以我在我得代码里增加了重试机制,如果请求序列化服务报错,那就重新发送请求。

Spring Retry

加重试机制很简单,将同样的代码copy一份,或者抽出来做成一个函数,调用多次就好。

但是这样不够优雅,而Spring Retry提供了一个优雅的方案来自动重试。下面讲讲Spring Retry的简单用法

添加依赖

使用Spring Retry除了要添加它本身的依赖之外,还需要添加spring-aspects依赖,因为retry机制底层是通过aop来实现的。

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
</dependency>

启用Retry

在SpringBoot配之类里增加@EnableRetry注解

@Retryable 注解

在业务代码中为需要重试的方法添加@Retryable注解

例子:

@Retryable(value = RuntimeException.class, maxAttempts = 3)
  public int testRetry() {
      int rand = RandomUtil.randomInt(10);
      System.out.println("random number: " + rand);
      if (rand > 3) {
          throw new RuntimeException("Random number is greater than 5");
      }
      return rand;
  }

@Recover 注解

如果重试一直失败,我们需要一个补偿机制,这时候加了@Recover注解的方法就是这个补偿机制

  @Recover
  public int recover(RuntimeException e) {
      return -1;
  }

另外,@Retryable注解里也需要指明使用哪一个Recover方法

@Retryable(value = RuntimeException.class, maxAttempts = 3, recover = "recover")


完整的示例代码:

package com.example.demoground.service;
import cn.hutool.core.util.RandomUtil;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
@Service
public class RetryService {
    @Retryable(value = RuntimeException.class, maxAttempts = 3, recover = "recover")
    public int testRetry() {
        int rand = RandomUtil.randomInt(10);
        System.out.println("random number: " + rand);
        if (rand > 3) {
            throw new RuntimeException("Random number is greater than 5");
        }
        return rand;
    }
    @Recover
    public int recover(RuntimeException e) {
        return -1;
    }
}