【问题标题】:Is "Dying is Awesome" preferred?“死是真棒”是首选吗?
【发布时间】:2010-10-09 06:12:01
【问题描述】:

最近我参加了 Jeffrey Richter 的 .NET 培训课程。他提到了一种编码策略“死是真棒”。也就是说,即使在程序或事件循环的根部也不要写“catch (Exception ex)”。如果抛出了一些没有处理的异常,就让进程死掉。

我不确定这是否正确。就个人而言,我更喜欢在执行的顶层使用“try {...} catch(Exception ex) {log and try to recover}”来包装。实际上,如果从 asXx 抛出任何异常,ASP.NET 不会死。如果它确实因异常而死,那么一个灵丹妙药的请求可以使整个服务保持沉默。

你怎么看?

【问题讨论】:

  • 无论您是尝试恢复还是让应用程序死机,我仍然会尝试记录错误,除非您确信,在您的上下文中,运行时将在事件中捕获足够的异常信息记录。

标签: .net exception architecture exception-handling crash


【解决方案1】:

我认为这取决于您正在运行什么样的应用程序以及“死亡”的后果是什么。对于许多客户端应用程序来说,死亡真是太棒了。对于服务器,通常不是那么多(吞下日志是合适的)。没有万能的解决方案。

【讨论】:

  • +1 完全同意没有万能的解决方案。在应用程序逻辑出现网络或 Web 处理程序错误时,您为什么要在没有充分理由的情况下关闭整个系统?医疗器械呢?我能想到其他 100 个案例。
  • 另一方面......死了怎么办 - 并允许看门狗进程重新启动应用程序?
  • 正确。我们有一个“死亡”的原则,但我们的顶层捕获所有意外异常并将死亡转化为更用户友好的体验。虽然没有“继续”。点击一个键重新启动应用程序。这是一个用于运输的嵌入式应用程序。
  • 特别是对于服务器应用程序来说,吞噬是不好的,因为之后服务器可能会以损坏的状态运行。最好是让它死掉,死后自动重启……内部状态干净正确。
【解决方案2】:

也称为进攻性编程。

您应该查看“Offensive Programming: Crash Early, Crash Often”。

这与Defensive Programming的规范完全不同的意识形态:

[防御性编程]旨在 确保一个持续的功能 一个软件尽管 上述软件的不可预见的使用。

我个人喜欢“早期崩溃,经常崩溃”的理念。

我见过太多了:

try  
{  
    // ... Huge block of code goes here  
}  
catch(Exception ex)  
{  
   // Do nothing...  
}  

这比用力撞车要糟糕得多。如果异常处理得当,那么一点防御性编程就可以了。

【讨论】:

  • 除非有充分的理由,否则这应该是一种解雇行为。
【解决方案3】:

这一切都取决于您如何处理异常。如果您只在发生真正异常的事情时抛出异常(而不是在数据库查询没有返回结果或格式错误时),那么您真的不需要捕获异常。因为您的程序不应该能够恢复,所以您可以恢复的任何东西都不是异常,而是错误。

【讨论】:

  • 是的,但是我的打印机不在那里怎么办。那是 exceptional 吗?我不认为它是。但是,我所知道的是一个叫 .Print() 的人,我无法满足他们的要求。所以我一般扔。我个人认为“异常例外”不是在哪里使用例外的好规则。当我不能做别人要求我做的事情时,我会扔掉。
  • 如果不是异常,那么至少在意料之中。如果这是预期的,您的程序应该正在处理这种情况。如果打印机不可用,您甚至不应该尝试打印。这是您可以计划的事情。为你不能做的事情抛出异常是非常懒惰的。如果你正在编写一个计算器,你永远不应该抛出除以零的异常。相反,您应该通过在使用前检查除数来确保它永远不会发生。
【解决方案4】:

听起来像古鲁斯语

这听起来像是 gurus 鼓吹的另一个一般指导方针,它本身并不是一个糟糕的建议。但是,该准则可以很容易地应用于不属于它的地方。我认为要记住您上面使用的关键短语是“一种编码策略”,因为这种策略在某些领域非常有用,但在其他领域可能非常有害。

Dying is 太棒了 - 如果你有很多紧密耦合的组件,它们的状态相互依赖,那么异常很容易成为灾难性事件。但是,您的目标之一应该是以这样一种方式进行编码,即单个故障不会导致整个系统瘫痪(注意目标)。

您如何看待以下因普通异常而死的应用程序:

  • 医疗器械
  • 发电厂
  • 入侵检测系统

对于您在 try / catch 中捕获的异常 - 您确实应该期待它们并处理它们。对于所有其他情况,最好快速失败到预测的运行级别。那么,如果您在网络或 Web 处理程序中,为什么不直接终止当前操作呢?你真的需要整个应用程序宕机吗?

如果您正在开发一个任务关键型且具有公共界面的应用程序,这一点变得越来越重要。如果存在可能导致应用程序崩溃的异常情况,那么这将成为黑帽黑客意图造成拒绝服务攻击的攻击媒介。

滥用此策略的边缘案例有点太大,无法给予太多赞誉。更好的方法是解决您的域问题。了解此策略的含义并将适当的部分应用于您的问题。

注意事项:我在正常运行时间和安全性至关重要的服务器端系统上工作。

编辑:我想我对“进程死亡”的含义感到困惑 - 这是对整个应用程序的引用还是只是正在运行的线程等?

【讨论】:

    【解决方案5】:

    Karl Seguin 说了以下关于异常处理的内容:

    1. 只处理您可以实际处理的异常
    2. 您对绝大多数异常无能为力

    很好的主题介绍。

    【讨论】:

      【解决方案6】:

      这很微软。

      MS 希望您将所有未处理的异常抛出到 WER。 (Windows 错误报告,当应用程序崩溃时,您可以将错误发送给微软的对话框)

      通过这种方式,您可以获得使用指标,并可以专注于导致客户悲伤的关键未处理异常。我相信这个想法是它迫使您考虑异常的来源。

      但我完全同意你的看法。即使你之后重新抛出,你总是会在根处捕获一个未处理的,记录发生了什么。唯一的问题是,如果您未处理的 ex 内存不足,在这种情况下,您对 log 的调用可能会失败,因为 JIT 无法分配更多内存等。我认为 C++ 程序通过占用内存来处理这个问题,在未处理的异常上释放它,然后运行一个非常紧凑的日志例程以尝试优雅地失败。

      【讨论】:

        【解决方案7】:

        如果您无法充分处理该问题——并且除了拒绝某种形式的错误输入(这包括您尝试从磁盘或网络读取的内容)之外,您可能无法处理该问题——那么您应该死。除非你 100% 确定你可以安全地继续,否则不要。

        但是,我不会简单地放过一个例外。抓住它并尽可能多地收集信息。如果您的程序操作了某种文档,请将其保存在以新名称下——它可能已损坏,您不想覆盖原始文档。

        【讨论】:

          【解决方案8】:

          我不熟悉 Richter 的材料,但我认为其哲学/理由是,在绝大多数情况下,程序实际上无法从未处理和意外的异常中恢复。

          然而,让程序在用户面前死掉并不是一个好主意——它会留下非常糟糕的印象。我更喜欢将未处理的异常记录到数据库中,通知开发人员并跟踪它们以确保它们得到修复。

          再一次,我正是为了这个目的而销售产品,所以我在这方面有点偏见!

          【讨论】:

            【解决方案9】:

            没有什么比堆栈跟踪更快地破坏用户信心了。至少,捕获异常并记录尽可能多的信息。然后向用户提供友好的消息和说明,说明如何解决问题或将问题报告给支持人员。

            这里担心继续处于不确定状态。如果这是一个网络应用程序,那么这不是问题,除非您过度依赖会话和应用程序状态。如果这是一个 Windows 应用程序,请随时退出(在让用户有机会保存之后)。

            【讨论】:

            • “在给用户一个保存的机会之后”。但至关重要的是,不要让用户保存现有文件,否则他们很有可能会用处于不确定状态(即被异常损坏)的文件替换文件的最后一个好副本.
            【解决方案10】:

            由于您不知道 Exception 的每个子类是什么,您根本无法知道捕获它们是可以的。因此,您应该注意自己的事情:捕获您知道和关心的异常,这主要是您为自己的程序显式创建的异常,或者为您正在使用的库调用中的错误创建的异常。特别是,捕获 Exception 类只是懒惰、糟糕的编程 --- 本质上,您是在说“我不在乎问题是什么,也不要告诉我;就这样做吧。”看看java程序是如何处理异常的,那里的异常处理实践通常都不错。

            【讨论】:

              【解决方案11】:

              您的客户期望什么?

              这一切都回到了这一点。如果客户可以处理程序死机并且他们可以重新启动它,那就这样吧。

              有时,最好让一个进程终止并创建另一个进程来处理请求。

              有时,最好尝试解决问题。

              【讨论】:

                【解决方案12】:

                我说你应该总是有一个根异常捕获器......特别是如果你可以在异常中加入某种信息,说明出了什么问题,代码或其他东西并将其打印给用户。然后,用户可以随时询问您(或支持或其他)出了什么问题并提供一些信息......而不是“它因保护错误而崩溃”

                【讨论】:

                  【解决方案13】:

                  根本不处理任何异常的好处之一。如果出现问题,您宁愿崩溃并让用户抱怨,而不是让程序在不确定的状态下继续运行。

                  例如,如果您正在编写一个实时交易系统,并且出现意外错误,您最好让它崩溃。否则,您的程序可能会继续运行并进行愚蠢的交易。用户会马上抱怨“WTF?”,但至少你不会损失数百万美元。

                  【讨论】:

                    【解决方案14】:

                    这里已经有了很棒的答案,尤其是 Simucal。我只想补充一点:

                    攻击性编程非常适合调试。根据我的经验,“早期失败,经常失败”可以被认为是为代码中的错误设置陷阱。当出现问题时(无论是内存泄漏、内存踩踏、意外 NULL 等),如果程序立即失败(以及相关的调试数据,例如调用堆栈),通常更容易发现问题。

                    防御性编程非常适合生产环境。应用程序发布后,您可能不希望最终用户看到一个漂亮的小对话框,上面写着“foo() 处未处理的异常”。如果您的应用程序处于测试阶段并且您仍在收集调试信息,那可能会很棒,但如果您正在发布一个稳定的应用程序,您可能只是不想在某些事情确实失败的罕见情况下用神秘的输出打扰用户。

                    有时你可以同时拥有它。我经常按照以下几行编写 C 代码:

                    void foo(char* data) {
                        ASSERT(data);
                        if (!data) { return; }
                        // ... work with data
                    }
                    

                    如果此代码是在测试环境中编译的,则 assert 语句会捕获错误条件。如果它是在生产环境中编译的,则断言会被预处理器删除,并且 foo() 会静默失败。

                    异常比断言更具表现力和灵活性,并且有多种方法可以管理它们,以便应用在测试环境中及早失败,并在生产环境中记录错误时继续运行。根据项目的类型,这种设计可能对您有意义。

                    【讨论】:

                    • "在测试环境中,assert 语句捕获错误条件。" ...然后我希望使用未初始化数据调用 foo 的代码得到纠正,因为它会产生错误?在我看来,这使得 if 测试毫无用处。
                    • 是的,这里的整个想法是使 if 测试变得多余,但是如果您使用 C 编写业务逻辑(不是该工作的最佳工具),您可能需要这种冗余。在野外发生的测试可能没有发现罕见的情况。
                    【解决方案15】:

                    try catch 解决所有问题的问题在于,您经常会“处理”一些您不知道如何从中恢复的事情。用户会误以为事情进展顺利,但在内部你会转向瑞士奶酪并最终以非常奇怪的方式打破。

                    另一方面,当用户也没有某种方式报告异常时,向用户抛出异常并没有太大帮助

                    • 新的 Windows 服务会在您注册 MS 时提供该功能。
                    • 安装 drwatson 还可以捕获数据(转储文件)供您的用户手动发送给您。

                    如果您提供了一个服务库并且它在进程中运行,如果您的服务与整个服务器混淆内存或设置等,那么当引发异常时服务器可能真的需要关闭。

                    按照约定,API 将倾向于提供一种方式来表示“我可以这样做”,然后提供一种方式来“执行”,并且许多 API(例如 MS 文件打开)将允许您在引发异常和返回异常之间进行切换错误代码。

                    【讨论】:

                      【解决方案16】:

                      您始终可以设置自定义错误处理程序并忽略捕获您不准备处理的异常。如果满足正确的条件,试图从未知中恢复可能也会产生负面影响。但话又说回来,这确实取决于异常是什么以及您如何处理它们。

                      另一方面 - 我认为人们会被整个“例外是邪恶的”心态所淹没。它们是一种工具,如果使用得当,可以带来惊人的好处。但是,许多人通过使用 catch(Exception) 包装 root 来滥用它们..

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 1970-01-01
                        • 2014-03-14
                        • 2013-06-11
                        • 2013-04-10
                        • 1970-01-01
                        • 1970-01-01
                        • 2014-08-02
                        • 2011-05-22
                        相关资源
                        最近更新 更多