gpt4 book ai didi

kotlin - CoroutineExceptionHandler 应该如何处理 OutOfMemoryError 或其他 fatal error ?

转载 作者:行者123 更新时间:2023-12-02 12:08:46 27 4
gpt4 key购买 nike

我正在实现一个自定义 Kotlin CoroutineScope,它处理通过 WebSocket 连接接收、处理和响应消息。范围的生命周期与 WebSocket session 相关联,因此只要 WebSocket 处于打开状态,它就会处于事件状态。作为协程范围上下文的一部分,我安装了一个 custom exception handler,如果出现未处理的错误,它将关闭 WebSocket session 。它是这样的:

val handler = CoroutineExceptionHandler { _, exception -> 
log.error("Closing WebSocket session due to an unhandled error", exception)
session.close(POLICY_VIOLATION)
}
我惊讶地发现异常处理程序不仅接收异常,而且实际上为所有未处理的 throwable 调用,包括 Error 的子类型。我不确定我应该如何处理这些,因为我从 Java API documentation for Error 中知道“ Error [...] 表示合理的应用程序不应该 try catch 的严重问题”。
我最近遇到的一个特殊情况是 OutOfMemoryError ,因为 session 处理的数据量很大。我的 OutOfMemoryError 收到了 CoroutineExceptionHandler ,这意味着它被记录并关闭了 WebSocket session ,但应用程序继续运行。这让我很不舒服,因为我知道 OutOfMemoryError 可以在代码执行期间的任何时候抛出,因此会使应用程序处于不可恢复的状态。
我的第一个问题是:为什么 Kotlin API 选择将这些错误传递给 CoroutineExceptionHandler 供我这个程序员处理?
我的第二个问题,直接从那之后,是:我处理它的合适方法是什么?我至少能想到三个选择:
  • 继续做我现在正在做的事情,即关闭引发错误的 WebSocket session ,并希望应用程序的其余部分可以恢复。正如我所说,这让我感到不舒服,尤其是当我阅读 this one, in response to a question about catching OutOfMemoryError in Java 之类的答案时,强烈建议不要尝试从此类错误中恢复。
  • 重新抛出错误,让它传播到线程。这就是我在任何其他情况下通常会在普通(或框架)代码中遇到 Error 的情况,因为它最终会导致 JVM 崩溃。但是,在我的协程范围内(与一般的多线程一样),这不是一个选项。重新抛出异常只会将它发送到线程的 UncaughtExceptionHandler ,它不会对它做任何事情。
  • 启动应用程序的完全关闭。停止应用程序感觉是最安全的做法,但我想确保我完全理解其中的含义。协程是否有任何机制可以将致命错误传播到应用程序的其余部分,或者我是否需要自己编写该功能? “应用程序致命”错误的传播是 Kotlin 协程 API 设计者已经考虑过的,或者可能在 future 版本中考虑的吗?其他多线程模型通常如何处理这些类型的错误?
  • 最佳答案

  • 为什么 Kotlin API 选择将这些错误传递给 CoroutineExceptionHandler 供我这个程序员处理?
    Kotlin docs on exceptions 状态:

    All exception classes in Kotlin are descendants of the class Throwable.


    因此,Kotlin 文档似乎对各种 Throwable 使用术语异常,包括 Error
    是否应该传播协程中的异常实际上是选择协程构建器的结果(参见 Exception propagation ):

    Coroutine builders come in two flavors: propagating exceptions automatically (launch and actor) or exposing them to users (async and produce).


    如果您在 WebSocket 范围内收到未处理的异常,则表明调用链中存在不可恢复的问题。期望在最接近的调用级别处理可恢复的异常。因此,很自然地,您不知道如何在 WebSocket 范围内做出响应并指出您正在调用的代码存在问题。
    然后协程函数选择安全路径并取消父作业(包括取消其子作业),如 Cancellation and exceptions 中所述:

    If a coroutine encounters an exception other than CancellationException, it cancels its parent with that exception. This behaviour cannot be overridden and is used to provide stable coroutines hierarchies for structured concurrency.


  • 我该如何处理?
    在任何情况下:尝试先记录它(就像你已经做的那样)。考虑提供尽可能多的诊断数据(包括堆栈跟踪)。
    请记住,协程库已经为您取消了作业。在许多情况下,这已经足够了。不要指望协程库做更多的事情(不是现在,也不是在 future 的版本中)。它没有知识可以做得更好。应用服务器通常提供异常处理的配置,例如就像在 Ktor 中一样。
    除此之外,它取决于,并且可能涉及启发式和权衡。不要盲目遵循“最佳实践”。您比其他人更了解您的应用程序的设计和要求。需要考虑的一些方面:
  • 为实现高效运营,在合理范围内自动、快速、无缝地恢复受影响的服务。有时,简单的方法(关闭并重新启动可能受影响的一切)就足够了。
  • 评估从未知状态恢复的影响。是否只是一个容易被发现的小故障,还是人们的生活取决于结果?在未捕获异常的情况下:应用程序是否以资源释放和事务回滚的方式设计?依赖系统能否继续不受影响?
  • 如果您可以控制调用的函数,您可能会为可恢复的异常(只有暂时和非破坏性的效果)引入一个单独的异常类(层次结构),并以不同的方式对待它们。
  • 尝试恢复部分工作的系统时,请考虑采用分阶段方法并处理后续故障:
  • 如果只关闭协程就足够了,就这样吧。您甚至可以保持 WebSocket session 打开并向客户端发送重新启动指示消息。考虑 Kotlin 协程文档中关于 Supervision 的章节。
  • 如果这不安全(或发生后续错误),请考虑关闭线程。这与分派(dispatch)到不同线程的协程无关,但对于没有线程间耦合的系统来说,这是一个合适的解决方案。
  • 如果这仍然不安全(或发生后续错误),请关闭整个 JVM。这一切都可能取决于异常的根本原因。

  • 如果您的应用程序修改了持久数据,请确保它的设计是防崩溃的(例如,通过原子事务或其他自动恢复策略)。
  • 如果您的整个应用程序的设计目标是防崩溃,请考虑使用 crash-only software design 而不是(可能是复杂的)关闭程序。
  • 在 OutOfMemoryError 的情况下,如果原因是一个奇点(例如一个巨大的分配),恢复可以如上所述分阶段进行。另一方面,如果 JVM 甚至不能分配微小的位,通过 Runtime.halt() 强制终止 JVM 可能会防止级联后续错误。

  • 关于kotlin - CoroutineExceptionHandler 应该如何处理 OutOfMemoryError 或其他 fatal error ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63535681/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com