春季重试-@Circuitbreaker未重试

时间:2018-10-23 14:49:40

标签: spring-retry circuit-breaker

我遇到了@CircuitBreaker无法重试的问题。

我有一个服务类(例如,类UserService和方法名称getUser),此方法调用另一个Spring bean(例如,AppClient和execute),后者又调用了远程服务(REST调用)。执行方法由Spring-Retry的@CircuitBreaker注释。

我公开了rest控制器中对服务方法(类UserService和方法名称getUser)的调用,并使用Postman对其进行了测试。这就是发生的情况-如果发生超时错误,它会调用@Recover方法。但它不会重试三次调用远程服务(默认值)。

如果我通过Postman手动运行3次,则断路器状态将变为OPEN,并将调用重定向到@Recover方法,并且在重置超时后,它将恢复对远程服务的调用。

此外,我将@CircuitBreaker替换为@Retryable,并且进行了三次调用(默认值)。我正在使用spring-retry 1.2.1.RELEASE版本和Aspectjtools 1.6.2。版本。

为什么不使用@CircuitBreaker重试?我想同时具有断路器和重试功能。根据文档,@ CircuitBreaker应该同时执行这两项操作。任何帮助将不胜感激。

@Configuration
@EnableRetry
public class cfgUserApp
{

}

@RestController
public class UserController
{
@Autowired
UserService userService;

@RequestMapping(value = "/user/{userId}", method = RequestMethod.GET, headers = "Accept=application/json")
public ResponseEntity<User> getUser(@PathVariable String userId) {
    return ok(userService.getUser(userId));
}
}

/* Spring Bean -- userService */

public class UserServiceImpl
implements userService
{

@Override
public User getUser( final String userId )
{
checkArgument( User != null && !User.isEmpty(), "User Id can not be null or empty." );
try
{
    final HttpGet request = buildGetUserRequest( userId );
    final User userResult = appClient.execute( request,
        response -> createGetReservationResult( response ) );
    return userResult;
}
catch ( final IOException e )
{
    LOG.error( "getUser failed.", e );
    throw new AppException(e.getMessage(), e);
}
}
}

public Class Appclient {

@Recover
public <T> T recover(AppException appException, final HttpUriRequest request,
                            final ResponseHandler<T> responseFunction )
{
    System.out.println("In Recovery");
    return <T>new User();
}

@CircuitBreaker( include = AppException.class, openTimeout = 5000l, resetTimeout = 10000l )
    public <T> T execute( final HttpUriRequest request,
                          final ResponseHandler<T> responseFunction )
                          {
                          // HTTP call
                          }
}

1 个答案:

答案 0 :(得分:0)

这可能与this类似的问题-使用JDK代理时找不到注释,因为您有interface

将注释移至界面,或使用

@EnableRetry(proxyTargetClass = true)

我向该PR添加了另一个提交以解决此问题。

编辑

您似乎误解了@CircuitBreaker;它不会在内部重试;相反,它是一个有状态的重试拦截器,在超出断路器属性后会进行故障转移。

我更改了您的应用以执行此操作...

@GetMapping("/getnumber")
public int getNumber(){
    return this.userService.getNumber() + this.userService.getNumber() +
            this.userService.getNumber() + this.userService.getNumber();
}

然后我看到

getNumber
fallback
getNumber
fallback
getNumber
fallback
fallback

要实现您想要的(我认为),您需要将服务与重试服务一起包装,然后在其中进行恢复:

@SpringBootApplication
@EnableRetry(proxyTargetClass = true)
public class DemoApplication {

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

@RestController
class UserRestController {

    private final RetryingUserService userService;

    @Autowired
    public UserRestController(RetryingUserService userService) {
        this.userService = userService;
    }

    @GetMapping("/getnumber")
    public int getNumber() {
        return this.userService.getNumber();
    }

}

@Service
class RetryingUserService {

    private final UserService userService;

    public RetryingUserService(UserService userService) {
        this.userService = userService;
    }

    @Retryable
    public int getNumber() {
        return this.userService.getNumber();
    }

    @Recover
    public int fallback(RuntimeException re) {
        System.out.println("fallback");
        return 2;
    }

}

@Service
class UserService {

    @CircuitBreaker(include = RuntimeException.class)
    public int getNumber() {
        System.out.println("getNumber");
        throw new RuntimeException();
    }

}

getNumber
getNumber
getNumber
fallback

或者,您可能希望将重试放在断路器中,具体取决于您想要的行为。