我应该从我的服务中抛出可重试异常吗?

3

我的服务是DService,我是服务链中的第四个链接,即调用流程为:在线用户-> AService-> BService-> CService-> DService-> EService。

当我从DService调用EService时,它可能会抛出可重试的异常,如HttpTimeoutException。我通常会重试2-3次,如果即使在2-3次重试后仍然失败,则会抛出异常。

我的问题是,我向CService抛出的异常应该是可重试还是不可重试?请查看以下两个选项的优缺点:

从DService抛出可重试异常的缺点 - 如果DService抛出可重试异常,按照同样的惯例,CService也可能会重试DService 2-3次,并且在每个C-D调用中,D将再次尝试2-3次EService调用。随着我们沿着调用链向上移动,最终到达EService的调用将呈指数增长。因此,如果EService网络确实长时间处于关闭状态,我们正在谈论大量不必要的调用。这可以通过为链中的每个调用设置超时来缓解,但不确定是否足以缓解不必要的调用次数。

从DService抛出可重试异常的优点 - CService将在一段时间后进行重试,因为在后续的重试中,我们可能会得到正确的值(在时间限制内) - 特别是如果客户端是某些后端作业,则它们可以指数级地重试很长时间,然后才放弃。抛出不可重试异常将淘汰这种选项

请提供您的意见和建议

谢谢, 哈里什

4个回答

3
不知道服务的具体功能,无法确定DService是否应该重试或者是CService。然而我的理念是被调用的服务不应该重试,永远不应该。在这种情况下,EService会愚蠢地抛出异常而没有任何处理。原因是链的末端应该是无状态的,并且不应该代表调用者做出决策。
在可接受和不可接受的范围内,调用者可以在一定程度上决定错误是否应该重新尝试。换句话说,如果EService尝试连接到数据库,而DService正在执行查找服务,则DService可能会在某个表中找不到某些信息时,检查另一个表。然而,由于EService无法连接到数据库,所以DService的范围只是简单地返回由CService请求的信息。
CService发出调用以检索某些信息后,根据其所做的工作,可能会收到数据库连接并在延迟后尝试多次重试,因为它正在对该数据执行批处理工作,并将继续重试,直到数据库恢复在线状态。或者,如果它正在检索要显示给用户的信息,则必须快速失败并通过向用户呈现漂亮的错误消息来处理数据库连接错误。
这完全取决于您的服务的功能及其职责所在的位置。同样,异常是否可重试也应该取决于调用者的必要性,而不是服务本身。将可重试的异常呈现给调用者并尝试一次是完全可行的。
希望这有所帮助!

谢谢。那确实有所帮助。如果EService能够识别数据库异常是一次间歇性的陈旧连接异常并重试,那会怎样呢?通常情况下它会成功,但是假设即使EService尝试了2-3次后仍然失败...在这种情况下,EService应该抛出一个异常,表示客户端可以重试,还是应该抛出一个异常,表示不需要重试? - Harish

2

我认为,如果你在链条上定义了指数级递增的重试间隔时间,抛出可重试异常是一种可行的方法。


1
我认为你不应该在DService中首先重试,因为正如你所说,如果每个服务都这样做,你可能会遇到麻烦。因此,让异常沿着调用堆栈向上传递,并让最外层的服务处理它;甚至可以是用户。
原理:为什么要由DService决定CService、BService或AService是否要重试呢?
然而,我认为这也取决于异常的频率和重试的成功率。如果异常经常发生,但通常在第一次或第二次重试后调用成功,那就与每天发生一次且大多数情况下重试是徒劳无功的异常不同。

1
你向调用者抛出的内容以及是否附带建议“但是你可以重试这个操作”,应完全由你的服务的预期语义来确定。
(此外,我从未听说过Java异常对象正式携带任何此类属性,但这可能是因为我落后了一点。)
编辑。
无论你是否“重试”失败的操作,都由你(仅由你)决定。 但是,如果你决定重试,那么你也有责任决定在多少次失败后停止重试并放弃,并且在那时向调用者抛出建议“重试”的异常绝对不明智。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接