捕获Hystrix后备抛出的异常?

时间:2016-11-08 19:49:11

标签: spring-cloud hystrix spring-cloud-netflix

我正在设计服务外观,我有一个如下所示的方法签名:

public Policy getPolicy(long policyId) throws PolicyNotFoundException

如果没有发生任何错误,则返回Policy对象(简单POJO)。如果未找到所请求的策略,则抛出已检查的异常PolicyNotFoundException(仅作为参考 - 当涉及应用程序中的异常处理的最佳实践时,我们遵循this article。)

服务外观层上方的层(在本例中为Spring MVC RestController)知道如何处理此类PolicyNotFoundException并返回适当的有效负载。

我试图通过这样的方式将其纳入HystrixCommand:

@HystrixCommand(groupKey = "PolicyService", fallbackMethod = "getPolicySafe", ignoreExceptions = { PolicyNotFoundException.class })
public Policy getPolicy(long policyId) throws PolicyNotFoundException {
    LOGGER.info("Getting policy {}", policyId);

    // Simulate some error condition for testing purposes
    throw new RuntimeException("Something happened!");
}

private Policy getPolicySafe(long policyId, Throwable t) throws PolicyNotFoundException {
    LOGGER.warn("Falling back to circuit-breaker for getting policy {}", policyId, t);
    throw new PolicyNotFoundException(policyId);
}

基本上我希望我的断路器只是表现得像原始查找找不到策略一样。我遇到的这个问题虽然是我从后备方法抛出的异常在某处翻译时丢失了。我最终在上面的层中看到的异常是命令方法抛出的RuntimeException,而不是fallback方法抛出的异常。有没有解决的办法?我不想改变原始方法的合同,也不希望上面的图层知道除了在找不到策略的情况下必须捕获PolicyNotFoundException之外的任何内容。此处需要的任何内容都应该在此服务外观层中捕获。

非常感谢任何和所有帮助。谢谢!

4 个答案:

答案 0 :(得分:3)

基于@spencergibb提供的链接 - 我可能在升级到Hystrix 1.5.7之后找到了解决方案。此代码按预期工作

PolicyRestController.java

@RestController
@RequestMapping("/policies")
public class PoliciesApi {
  private static final Logger LOGGER = LoggerFactory.getLogger(PoliciesApi.class);

  @Autowired
  private PolicyService policyService;

  @RequestMapping(value = "/{policyId}", method = RequestMethod.GET, produces = { MediaTypes.POLICY_JSON_VALUE, MediaTypes.POLICY_XML_VALUE })
  public Policy getPolicy(@PathVariable long policyId) {
    try {
      // This just shown for simplicity. There is more to this method (input validation/etc)
      return this.policyService.getPolicy(policyId);
    }
    catch (PolicyNotFoundException ex) {
      // NotFoundException is a RuntimeException annotated with @ResponseStatus(HttpStatus.NOT_FOUND)
      // So the service returns a 404 to the client
      LOGGER.info("Policy {} wasn't found", ex.getPolicyId(), ex);
      throw new NotFoundException(String.format("Policy %s was not found", ex.getPolicyId()));
    }
  }
}

PolicyService.java

public interface PolicyService {
    @Cacheable("allPolicies")
    public List<Policy> getPolicies();

    @Cacheable("policies")
    public Policy getPolicy(long policyId) throws PolicyNotFoundException;
}

PolicyServiceImpl.java:

@Service
public class PolicyServiceImpl implements PolicyService {
  @HystrixCommand(groupKey = "PolicyService", fallbackMethod = "getPolicySafe", ignoreExceptions = { PolicyNotFoundException.class })
  public Policy getPolicy(long policyId) throws PolicyNotFoundException {
    LOGGER.info("Getting policy {}", policyId);

    // Simulate some error condition for testing purposes
    throw new RuntimeException("Something happened!");
  }

  @HystrixCommand(groupKey = "PolicyService", ignoreExceptions = { PolicyNotFoundException.class }, raiseHystrixExceptions = { HystrixException.RUNTIME_EXCEPTION })
  private Policy getPolicySafe(long policyId) throws PolicyNotFoundException {
    // Here is we hit our fallback we want to log a warning & simply act as if the policy wasn't found by throwing the same contingency exception as the API does
    LOGGER.warn("Falling back to circuit-breaker for getting policy {}", policyId);

    throw new PolicyNotFoundException(policyId);
  }
}

答案 1 :(得分:1)

这是hystrix的默认行为。 “如果命令有一个回退,那么只有第三个例子才会将三重回退逻辑传播给调用者”

请参阅错误传播部分here

答案 2 :(得分:1)

虽然你的解决方案可能适合你,但我注意到你的代码有些奇怪(我无法检查我的假设,所以我想请你检查一下。)

  1. 尽量避免在代码中使用已检查的异常,因为它是 难以维持。
  2. 根据您的代码,您永远不会抓到 “PolicyNotFoundException”,因为您正在使用raiseHystrixExceptions = {HystrixException.RUNTIME_EXCEPTION},这意味着您将不会获得自定义异常,以便传播HystrixRuntimeException。尝试重写您的代码,如下所示它应该 简化代码并修复一些问题:
  3. @Service
    public class PolicyServiceImpl implements PolicyService {
      @HystrixCommand(groupKey = "PolicyService", fallbackMethod = "getPolicySafe")
      public Policy getPolicy(long policyId) throws PolicyNotFoundException {
        LOGGER.info("Getting policy {}", policyId);
        throw new PolicyNotFoundException(); // throw real PolicyNotFoundException if policy is absent for the given id
      }
    
      @HystrixCommand(groupKey = "PolicyService")
      private Policy getPolicySafe(long policyId) throws PolicyNotFoundException {
        // Here is we hit our fallback we want to log a warning & simply act as if the policy wasn't found by throwing the same contingency exception as the API does
        LOGGER.warn("Falling back to circuit-breaker for getting policy {}", policyId);
    
        throw new PolicyNotFoundException(policyId);
      }
    }
    

答案 3 :(得分:0)

我这样做:

@Component
public class HystrixClient {

  @HystrixCommand(ignoreExceptions = {ClientArgumentException.class})
  public POJO getPojo(String id)
        throws ClientNoDataFoundException, ClientArgumentException, ClientGeneralException {

    //call my service and return POJO
  }
}

@Component
public TrueClientUsedForAnotherSerivce {

  @Autowired
  HystrixClient hystrixClient;

 public POJO getPojo(String id)
        throws ClientNoDataFoundException, ClientArgumentException, ClientGeneralException, ClientOpenCircuitException {
    try {           
        POJO result = hystrixClient.getCellular(id);            

        return result;
    }
    catch(HystrixRuntimeException e) {
        LOG.debug("The circuit is open");
        throw new ClientOpenCircuitException("Open circuit");
    }
}   

仅当@HystrixCommand方法在另一个类中时,它才有效。