【问题标题】:How resilient should my web app be? [closed]我的网络应用程序应该有多大的弹性? [关闭]
【发布时间】:2010-06-30 19:09:32
【问题描述】:

最近,我发现自己与我的老板就我们的网络应用程序(一个 c# asp.net MVC 应用程序)中的异常处理发生了很多争论。

基本上对话是这样的:

老板:“我们的程序出了点问题,客户x的数据库今天宕机了,大家都看到了错误页面。”

我:“应用程序中的大多数页面都使用数据库来做一些事情(错误页面除外),除了显示错误页面之外没有其他合理的选择。”

老板:“我们的应用程序应该更有弹性——应用程序中不需要数据库访问的部分应该仍然可以工作。”

通常情况如此极端,但有时我们会遇到与其他服务集成的情况,我们仍然可以安全地显示页面的其他部分或完成操作,尽管有一些烦人的代码后面的代码部分需要稍后使用可能失败的操作结果。如果有很多可能的故障点,这可能会变成一些极其难以管理的代码。

一般来说,对于“正常”的 Web 应用程序(不是关键任务等),“优秀”的开发人员会花费多少时间来尝试使他们的代码有足够的弹性来处理这些情况。我的老板似乎认为代码应该能够处理几乎任何情况(你不能只捕获异常吗?)。当有很多可能的故障点时,我不明白这怎么可能是经济的。

【问题讨论】:

  • 在这个经济环境中,按老板说的做。
  • 数据库示例可能有点极端和过于简单化。基本上,我们有一些页面与多个 3rd 方服务交互 - 地理编码/facebook/等......在页面执行过程中可能会对给定服务进行多次调用。一个服务的结果可以稍后传递给另一个服务或用于页面的呈现。在存在许多此类服务的情况下,假设其中任何一个都可能出现故障并试图在每种情况下产生合理的输出似乎变得非常困难。
  • 您的错误页面有多大用处。如果有一刻,您是最终用户,您希望看到什么?

标签: c# asp.net asp.net-mvc exception-handling


【解决方案1】:

我会留给老板决定。告诉他估计需要多长时间才能使应用“恢复弹性”,他会决定是否值得投资。

【讨论】:

  • 事实上,这是我通常采用的方法。它通常非常有效,尽管我有时会感觉到我的估计被夸大了,这应该是一个很少发生的“只是捕捉异常”的问题。
【解决方案2】:

当主数据库(即为 99% 的页面提供支持的数据库)关闭时,我开发的大多数数据驱动型应用程序都会重定向到自定义错误页面。 (不过,您确实希望向他们展示自定义屏幕,而不仅仅是让他们获得服务器错误页面)

对于外部服务,例如访问 SMTP 服务器或应用程序的大部分其他部分未使用的数据库,我们通常会在其周围编写代码,如果服务/数据库关闭,则仅显示页面反馈/无法访问。

不过,这确实取决于客户/利益相关者,只需确定他们希望在数据库关闭时发生什么并为他们执行此操作即可。这需要时间,但不应导致无法维护的应用程序或任何其他编码恐惧。

【讨论】:

    【解决方案3】:

    我想我会有点担心您丢失数据库的频率会导致此问题出现。即使在非任务关键型应用程序中,如果您每周丢失数据库超过一次,在我担心在应用程序的用户端制定解决问题的方法之前,我会看到我可以做些什么来改进它.

    话虽如此,我公司的最佳实践包括编码,以便在用户端优雅地故障转移到输出中的“无法连接”消息,而不是完整的 404 类型错误。我发现它真的不会给编码过程增加超过几分钟的时间,而且不激怒用户的价值非常值得“成本”

    【讨论】:

    • 也许数据库示例不是一个好示例,因为它有点极端。这通常是与第三方网络服务等集成的问题。
    【解决方案4】:

    您的老板应该在创建应用程序之前与客户进行讨论。像“非关键”这样的术语应该定义为一个数字(正常运行时间的百分比)。一些应用程序需要应用程序的不同部分具有不同数量的正常运行时间(您的老板建议),并且应用程序作为一个整体是向上/向下的(它现在是如何工作的)。 “弹性”应用程序可能会以与“正常”应用程序不同的方式(分布式读取/异步写入等)编写,因此(可能)很难转换“正常”应用程序。

    一位好老板与应用程序的客户讨论一个好的 SLA,并在开发开始前将其告诉开发人员。另一方面,一个好的开发人员应该在开始开发之前向他的老板抱怨不完整的需求。当需求中没有提及可用性时,需求是不完整的。

    当现有应用程序的可用性或可扩展性要求发生显着变化时,可能很难使应用程序中的更改获利。当你有一个好老板时,只有当应用程序比最初估计的成功得多(比如用户数量增加 100 倍)时,这些要求才会发生显着变化。在这种情况下,巨大的成功将产生足够的资金,从而使重写应用程序的大部分内容变得有利可图。

    【讨论】:

      【解决方案5】:

      这取决于...许多网页显示和呈现单独的“小部件”。每个小部件可能来自不同的数据源……有些甚至可能是完全静态的。我同意你老板的观点,如果加载一个小部件由于某种原因失败,我们仍然应该尝试加载页面的其余部分。一个失败的小部件不应阻止用户与网站的其余部分进行交互。

      作为 StackOverflow 的示例,假设这些答案右侧的“相关”部分无法加载,或者页脚由于某种原因无法加载。我会说最好只在页面的这些部分显示一些错误消息并仍然加载您的问题和答案,因为这些应该不受影响。

      现在,在某些情况下,加载所有这些“小部件”的代码会失败......或者您的框架使用的某些数据源会失败。在这些情况下,我认为显示一个通用的错误页面是合理的……如果框架或“小部件”加载代码由于某种原因而失败,这确实是一个可能无法处理的例外情况。

      总之,我基本同意你老板的说法,应该考虑和处理尽可能多的错误情况......并且我们应该向用户展示任何可能的情况。

      【讨论】:

        【解决方案6】:

        鉴于您继承了此代码,这可能是一个更学术的答案。如果您使用,或者更确切地说,如果有人使用过 Microsoft.Practices.EnterpriseLibrary.ExceptionHandling ExceptionPolicy 模式,那么从显示错误页面(通过抛出异常)切换到吃异常和显示空网格、列表等非常容易。

        您可能已经知道这个小模式,但无论如何:

                try
                {
                    //get data
                }
                catch (Exception ex)
                {
                    if (ExceptionPolicy.HandleException(ex, "Data Access Exception"))
                        throw ex;
                }
        

        【讨论】:

          【解决方案7】:

          当一个空的数据集和一个“用户友好”的错误消息会做时,你可能只是显示一个通用的错误页面。

          因此,例如,如果您正在显示用户/消息/日期列表,但您收集它的服务已关闭,您可能会显示一个空集。

          所以不要显示:

          500 Server Error
          
          
          
          
          .
          

          您可以显示如下内容:

           User   |    Message     | Date 
           ------------------------------
              No data available* 
          
          
          
          
          * Xyz service is be down. 
          

          您的应用仍然无法运行,因为数据不可用,但您可以放置​​一个没有数据的占位符,而不是将其扔给最终用户。

          这取决于您使用的内容,但一般来说可能很简单:

           List<Data> data = EmptyList<Data>();
          
           try 
           {
               data = service.GetData();
           } catch( ServiceUnavailableException error ) 
           {
               errorPage.SetMessage( service.GetName() + " service is down ");
               // log the error message
              logger.doLog( error );
           } 
          

          也就是说,将您的列表或任何结构初始化为空,然后用服务填充,如果失败(然后列表将保持为空)添加错误消息(请用户友好)并记录异常。

          【讨论】:

            【解决方案8】:

            我认为,一旦我们开始实践某些事情,构建弹性应用程序,例如,我们会花很少时间将其归类为正常或关键任务,并开始构建弹性、高可用性、可扩展等应用程序来处理故障。

            肯定需要付出更多努力来确保应用具有弹性,但是一旦获得并实施了这些知识, 下一次,它变得容易多了。 像 spring 或 Netflix 的断路器这样的通用库已经可用,它们在集成时可以在某些方面优雅地处理故障。 重试幂等操作也很容易实现。

            要使应用程序可用,它必须可用且可靠。 几乎所有应用程序(独立桌面实用程序除外)都是 2 层或更多层应用程序,这意味着将存在网络调用以及所有与 n/w 相关的问题。 因此,一旦您开始练习,您将开始在所有应用程序中实施弹性模式。

            【讨论】:

            • 这个问题本身(现在)离题了(注意它是在 7 年前发布的)。即使是公认的答案也只是一种意见。请不要添加更多意见(您的意见属于 n 层,不考虑现代微服务架构)。另外,我删除了指向您博客的链接 - 这不是放置它的地方。
            猜你喜欢
            • 1970-01-01
            • 2012-02-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-10-04
            • 1970-01-01
            相关资源
            最近更新 更多