最近,我发现自己和我的老板在我们的网络应用程序(c#asp.net MVC应用程序)中处理异常时发现了很多争论。
基本上,对话就是这样的:
老板:“我们的程序有问题,客户端x的数据库今天关闭,每个人都看到了错误页面。”
我:“大多数应用程序中的每个页面都使用数据库(错误页面除外),除了显示错误页面之外没有其他合理的选择。”
Boss:“我们的应用程序应该更具弹性 - 应用程序中不需要数据库访问的部分仍然可以运行。”
通常情况下这种情况极端,但有时我们遇到的情况是我们正在与另一个服务集成,我们仍然可以安全地显示页面的其他部分,或者完成操作,尽管有一些烦人的代码稍后部分代码需要稍后使用可能失败的操作结果。如果有许多可能的失败点,这可能会变成一些非常难以管理的代码。
一般来说,对于“正常”的Web应用程序(不是任务关键等等),“好”开发人员花了多少时间来尝试使其代码具有足够的弹性来处理这些情况。我的老板似乎认为代码应该能够处理几乎任何情况(你不能只捕获异常吗?)。当有许多可能的失败点时,我不明白这是多么经济。
答案 0 :(得分:12)
我会留给老板来决定。告诉他一个小时的估计,让应用程序“有弹性”需要多长时间,他会决定是否值得投资。
答案 1 :(得分:7)
当主数据库(即支持99%页面的数据库)关闭时,大部分数据驱动的大多数应用程序都被重定向到自定义错误页面。 (你确实希望向他们展示一个自定义屏幕,而不仅仅是让他们获得服务器错误页面)
对于外部服务,如点击SMTP服务器或未被应用程序其余部分使用的数据库,我们通常会在其周围设置代码,如果服务/数据库已关闭,则只显示页面反馈/不可访问。
它确实是客户端/利益相关者,只是确定他们想要在数据库关闭时发生什么并为他们做。这需要时间,但不应该导致无法维护的应用程序或任何其他编码恐怖。
答案 2 :(得分:3)
我想我有点担心您丢失数据库的频率会导致出现这个问题。即使在非任务关键型应用程序中,如果您每周丢失一次数据库超过一次,我会看到在我担心在应用程序的用户端解决问题的方法之前我可以做些什么来改进它
话虽这么说,我公司的最佳实践包括编码,以便诸如数据库错误之类的内容将在用户端优雅地故障转移到输出中的“无法连接”消息,而不是完全成熟的404类型错误。我发现它在编码过程中确实没有增加超过几分钟的时间,并且不会激怒用户的价值非常值得“花费”
答案 3 :(得分:3)
在创建应用程序之前,您的老板应该与客户讨论过。像“非关键”这样的术语应该被定义为一个数字(正常运行时间的百分比)。某些应用程序要求应用程序的不同部分具有不同的正常运行时间(您的老板建议的内容)和应用程序整体上/下(现在如何工作)。 “弹性”应用程序可能会以不同的方式(分布式读取/异步写入等)写入,而不是“普通”应用程序,因此(可能)很难转换“正常”应用程序。
一位优秀的老板与应用程序的客户讨论了一个良好的SLA,并在开发之前告诉开发人员。另一方面,一个优秀的开发人员应该在他的老板抱怨之前,在开始开发之前需要不完整的要求。如果没有提及要求中的可用性,则要求不完整。
当现有应用程序的可用性或可伸缩性要求发生显着变化时,可能很难使应用程序中的更改获利。当你有一个好老板时,这些要求只会在应用程序比最初估计的成功更多(比如用户多100倍)时显着改变。在这种情况下,巨大的成功将产生足够的资金,使重写应用程序的大部分有利可图。
答案 4 :(得分:2)
这取决于......许多网页显示并呈现单独的“小部件”。每个小部件可以从不同的数据源绘制......有些甚至可能是完全静态的。我同意你的老板的观点,如果因为任何原因加载一个小部件失败,我们仍然应该尝试加载页面的其余部分。一个失败的小部件不应该阻止用户与网站的其余部分进行交互。
作为StackOverflow的一个例子,假设这些答案右侧的“相关”部分无法加载,或者页脚由于某种原因无法加载。我会说最好只在页面的这些部分显示一些错误信息,并仍然加载你的问题和答案,因为这些应该不受影响。
现在,有些情况下加载所有这些“小部件”的代码都会失败......或者框架使用的某些数据源失败。在这些情况下,我认为显示一般错误页面是合理的......如果框架或“小部件”加载代码因任何原因失败,这确实是一个可能无法处理的特殊情况。
总而言之,我大多同意你的老板,并说应该考虑和处理尽可能多的错误案例......我们应该向用户展示任何可能的错误。
答案 5 :(得分:2)
鉴于您继承了此代码,这可能是一个更为学术性的答案。如果您使用,或者更确切地说,如果有人使用了Microsoft.Practices.EnterpriseLibrary.ExceptionHandling ExceptionPolicy模式,那么很容易从显示错误页面(通过抛出异常)切换到吃异常并显示空网格,列表等。
您可能已经意识到这种小模式,但无论如何它仍然存在:
try
{
//get data
}
catch (Exception ex)
{
if (ExceptionPolicy.HandleException(ex, "Data Access Exception"))
throw ex;
}
答案 6 :(得分:1)
当一组空数据带有“用户友好”错误信息时,你可能只是显示一般错误页面。
因此,例如,如果您显示的是用户/消息/日期列表,但是您收集它的服务已关闭,则可能显示空集。
所以不要显示:
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 );
}
也就是说,将列表或任何结构初始化为空的,然后填写服务,如果失败(然后列表将保持为空)添加错误消息(请用户友好)并记录异常。
答案 7 :(得分:-1)
我认为,一旦我们开始练习某些内容,例如构建弹性应用程序,我们就会花费很少的时间将其归类为正常或关键任务,并开始构建弹性,可扩展等应用程序以应对失败。
确保弹性应用程序确实需要更多努力,但是一旦获得并实施了这些知识, 下一次,它变得容易多了。 像spring或Netflix的断路器这样的通用库已经可用,在集成时,可以在某些方面优雅地处理故障。 重试幂等操作也很容易实现。
要使应用程序可用,它必须可用且可靠。 几乎所有应用程序(独立桌面实用程序除外)都是2个或更多分层应用程序,这意味着网络调用将存在并且所有与n / w相关的问题。 因此,一旦开始练习,您将开始在所有应用程序中实现弹性模式。