2016-11-08 378 views
0

我设计一个服务的门面,我有一个方法签名,看起来像这样:Hystrix fallback引发的捕获异常?

public Policy getPolicy(long policyId) throws PolicyNotFoundException 

如果什么也没有发生,然后策略对象(PO​​JO简单)被返回。如果未找到所请求的策略,则会引发检查异常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); 
} 

基本上我想要我的断路器简单地表现为,如果政策不是由原始的查询发现。我遇到的问题是,我从fallback方法抛出的异常在翻译的某处丢失。我在上面的图层中看到的异常是由命令方法抛出的RuntimeException,而不是fallback方法抛出的异常。有没有解决的办法?我不想更改原始方法的合同,也不希望上面的图层知道除了在未找到策略的情况下必须捕获PolicyNotFoundException之外的任何内容。无论这里需要什么,都应该在这个服务门面层中捕获。

任何和所有的帮助将不胜感激。谢谢!

回答

1

这是hystrix的默认行为。 “如果命令有一个回退,那么只有第一个例外,即trigers fallback逻辑将传播给调用者”

请参见错误传播部分here

+0

对的,我理解这一点。有没有办法改变这种行为? – Eric

+2

关于异常的讨论在这里https://github.com/Netflix/Hystrix/issues/1344 – spencergibb

+0

感谢您的链接 - 我没有看到。虽然这是朝正确方向迈出的一步,但并未完全解决问题。应用程序的服务门面层就是所有这些hystrix“东西”都起作用的地方。此解决方案“污染”上面的图层并使服务外观层的调用者必须知道Hystrix所涉及的具体细节,方法是捕获HystrixRuntimeException,查找后备异常(通常为FallbackInvocationException),然后必须阅读由此引起的。 – Eric

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. 尽量避免在代码中使用已检查的异常,因为它的 难以维护。
  2. 根据您的代码,您将永远不会捕获 “PolicyNotFoundException”,因为您正在使用raiseHystrixExceptions = {HystrixException.RUNTIME_EXCEPTION}这意味着您将不会获取您的自定义异常,以便传播HystrixRuntimeException。尝试重写代码如下所以应该 简化代码,也许解决一些你的问题:

@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); 
    } 
} 
+0

感谢提示。这在电路断开的情况下仍然不起作用。当电路打开时,HystrixRuntimeException被传播给调用者(在我的例子中是一个Spring MVC RestController)。这“污染”了我的RestController类,因为必须包含HystrixRuntimeException。我想在所属的服务门面层中保留所有与hystrix相关的“东西”。 – Eric

+0

@Eric因此逻辑上它是正确的,因为异常是由hystrix平台提出的。它是预期的行为,所以你必须在这种情况下捕获HystrixRuntimeException。 – dmgcodevil

+0

虽然应用程序中的某些其他层需要知道Hystrix甚至参与其中,但它只是觉得“脏”。如果我在我的RestController和服务外观图层之间有一个接口,它说 '公共策略getPolicy(long policyId)抛出PolicyNotFoundException' 该方法的调用者不应该也必须知道也许Hystrix在图片中,并且可能需要捕捉一个HystrixRuntimeException并从中解开PolicyNotFoundException ... – Eric