繁体   English   中英

在不应崩溃的关键应用程序中处理异常

[英]Handling Exceptions in a critical application that should not crash

我有一个正在调试的服务器应用程序,它基本上为请求它的应用程序解析脚本(VBscript,Python,Jscript和SQl)。

这是一个非常关键的应用程序,如果崩溃,它会对许多用户造成破坏。 我面临的问题是如何处理异常,以便应用程序可以继续运行,并且用户知道脚本中是否存在错误。

一个示例:在SQL脚本中,应用程序通常返回一组值(日期,数字,字符串和数字)。 因此,脚本必须在末尾声明如下:

into dtDate, Number, Number, sString 这些是内置在应用程序中的值,服务器应用程序知道如何解释这些值。 这些字段在服务器应用程序中被视为数组的一部分。 返回值通常应按特定顺序排列,因为这些字段在数组中的索引已在服务器应用程序中进行了硬编码。

现在,当编写脚本的用户忘记了这些字段之一时,最后一个字段(通常为字符串)将抛出IndexOutofBoundsException。

问题是如何从这种性质的异常中恢复而不删除应用程序?

另一个示例是脚本中的错误,无法为其生成错误解析消息。 这些错误只会在应用程序的后台消失,并最终导致服务器应用程序崩溃。 失败的脚本不一定完全不能执行,但是一部分不能执行,而其他部分可以执行,这使得它对用户来说看起来很奇怪。

此服务器应用程序是本机C ++应用程序,并使用COM技术。

我想知道是否有人对最好的处理异常的方法有什么想法,例如上述的异常而不会导致应用程序崩溃?

您无法处理带有异常的此类问题。 您可能会有一个顶级catch块来捕获异常,并希望程序的状态不会过多,以使程序保持活动状态。 仍然不能使用户满意,她正在等待的查询仍然无法运行。

确保更改不会破坏关键业务应用程序的稳定性,这需要组织。 批准更改的人员,并在允许将其投入生产之前验证它们是否按预期工作。 QA。

因为您谈论解析不同的语言,所以您可能会遇到类似

class IParser //parser interface
{
  virtual bool Parse( File& fileToParse, String& errMessage ) = 0;
};

class VBParser : public Parser
class SQLParser : public Parser

假设Parse()方法引发未处理的异常,则整个应用程序将崩溃。 这是一个简化的示例,可以在应用程序级别解决此问题:

  //somewhere main server code
void ParseFileForClient( File& fileToParse )
{
  try
  {
    String err;
    if( !currentParser->Parse( fileToParse, err ) )
      ReportErrorToUser( err );
    else
      //process parser result
  }
  catch( std::exception& e )
  {
    ReportErrorToUser( FormatExceptionMessage( err ) );
  }
  catch( ... )
  {
    ReportErrorToUser( "parser X threw unknown exception; parsing aborted" );
  }
}

如果您知道某个操作会引发异常,则需要在此区域添加异常处理。

基本上,您需要以异常安全的方式编写代码,通常使用以下准则

  • 处理可能引发异常的临时值
  • 在之后使用临时值提交更改(通常不会引发异常)

如果在处理临时值时抛出异常,则不会破坏任何内容,并且在异常处理中,您可以管理情况并恢复。

http://www.gotw.ca/gotw/056.htm

http://www.gotw.ca/gotw/082.htm

这实际上取决于启动服务器应用程序需要多长时间。 让应用程序崩溃然后重新加载它可能更安全。 或者从Chrome浏览器中获取提示,可能会导致崩溃的不同进程运行应用程序的不同部分。 如果您可以安全地恢复异常并相信您的应用程序状态正常,则可以这样做。 但是,捕获std :: exception和继续操作可能会有风险。

有简单到复杂的方法可以使流程坐下,以确保它们崩溃后可以重新启动。 我使用了几个工具。

bluepill http://asemanfar.com/Bluepill:-a-new-process-monitoring-tool

起搏器http://www.clusterlabs.org/

对于由于用户错误而可能在程序内部发生的简单异常,只需保存可以更改的状态,然后像这样恢复它:

SaveStateThatCanBeAlteredByScript();
try {
    LoadScript();
} catch(std::exception& e){
    RestoreSavedState();
    ReportErrorToUser(e);
}
FreeSavedState();

如果要防止外部代码崩溃(可能是不可信任的代码,如插件),则需要IPC方案。 在Windows上,我认为您可以使用OpenFile()来存储映射文件。 在POSIX系统上,可以将sem_open()mmap()一起使用。

如果您有服务器。 基本上,您有一个等待信号开始工作的主循环。 信号可能什么都不是,您的服务器只是浏览文件系统上的文件列表,或者更像是Web服务器,它在其中等待连接并执行连接上提供的脚本(或类似的东西)。

MainLoop()
{
    while(job = jobList.getJob())
    {
         job.execute();
    }
}

要阻止服务器因脚本而崩溃,您需要将外部作业封装在受保护的区域中。

MainLoop()
{
    // Don't bother to catch exceptions from here.
    // This probably means you have a programming error in the server.
    while(job = jobList.getJob())
    {
        // Catch exception from job.execute()
        // as these exceptions are generally caused by the script.
        try
        {
            job.execute();
        }
        catch(MyServerException const& e)
        {
            // Something went wrong with the server not the script.
            // You need to stop. So let the exception propagate.
            throw;
        }
        catch(std::exception const& e)
        {
            log(job, e.what());
        }
        catch(...)
        {
            log(job, "Unknown exception!");
        }
    }
}

如果服务器对您的操作至关重要,则仅检测问题并进行记录并不总是足够的。 写入错误的服务器将崩溃,因此您要自动执行恢复。 因此,您应该编写某种形式的心跳进程,以定期检查这些进程是否崩溃以及是否已自动重启。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM