【问题标题】:Design Pattern for gracefully failing if a database is missing数据库丢失时优雅失败的设计模式
【发布时间】:2012-03-23 03:45:40
【问题描述】:

我有一个正在开发的应用程序,它使用数据库来实现其功能的子集。在开发过程中,我们对数据库不可用的预期高于正常预期。一旦启动,即使数据库中断,我们仍然希望应用程序的主体能够正常工作。

我所有面向数据库的类都扩展了一个基类,该基类授予它们数据库访问权限,但是在具体类中,有一些方法直接执行selects(意味着不通过基类方法)。

没有将每个数据库调用包装在 if 语句中,当数据库不可用时如何优雅地降级功能?

编辑

此应用程序正常失败将显示“服务不可用”消息并阻止类尝试访问数据库(这会引发错误)。

【问题讨论】:

  • 优雅地失败到底是什么意思?你想显示缓存数据之类的东西,通知服务暂时不可用,只是不显示这条信息?
  • @Mahok - 在这种情况下通知服务已关闭是合适的。更新问题

标签: php oop design-patterns pdo


【解决方案1】:

恕我直言,这属于一般错误和异常处理的更广泛范围。

一些介绍......

数据库本质上是代码中的全局变量输入。如果它不可用,这就是意外的“异常”条件的定义;它应该导致异常。当然,您明确询问了如何处理这种情况“没有将每个数据库调用包装在 if 语句中。” 将每个 DB 操作包装在 try/catch 块中基本上与包装每个操作相同在if 语句中。

此外,mysql_connect 函数如果无法连接,除了返回 FALSE 外,还会引发 PHP 错误。你真的应该使用PDO 而不是mysql 恐龙,但这完全是另一个话题。希望您没有使用错误抑制运算符 @ 或不报告 E_WARNING 错误以避免这种情况。

那么如何处理...

就个人而言,我与 PHP 中的错误处理无关。您应该使用set_error_handlerdocs 定义一个自定义错误处理函数,该函数会在任何 PHP 错误上引发异常并处理异常而不是错误

当您以这种方式设置错误处理策略时,可以轻松包装任何可能导致问题或意外抛出 try 块的功能,并处理 catch 块中的意外事件。

既然你说过,“所有[你的]面向数据库的类都扩展了一个基类,授予它们数据库访问权限,”我建议在基类中添加一个简单的布尔属性来执行作为数据库连接是否失败的标志。然后,只需在每次尝试查询数据库之前检查标志的值。如果您在类中正确传递数据库实例(而不是破坏引用),那么它们在需要时都可以访问相同的属性。

如果数据库连接失败,您只需在 try 块中设置假设的 $dbConn->connError = TRUE; 标志。

因此,总而言之,虽然如何构建实际代码的具体细节取决于您正在做什么,但恕我直言,使用异常来处理错误情况是最可靠的方法。

【讨论】:

  • 您误读了这个问题。我正在使用 PDO。我将连接包裹在 try/catch 中,并正在处理那里引发的异常。如果 PDO 连接不可用,这并不能防止 PDO::exec 或 PDOStatement::execute 在应用程序的其他地方报告错误。我想要一种方法来集中处理这类问题,首先防止它们发生。
  • @RobAllen 哦,对此表示歉意。我不确定我在哪里看到了对mysql_query 的引用。 必须是“星期一”的情况。我将把它留在答案中,作为对其他路人的教育点。在我看来,在发生连接错误时设置一个简单的布尔标志并在查询数据库之前检查它仍然可以正常工作。
【解决方案2】:

AOP 有可能吗?这可以让您编写拦截连接到数据库的代码的任何部分的代码,并插入您的错误处理代码。

http://code.google.com/p/php-aop/

【讨论】:

  • 可能...但是我需要更多信息,因为我以前没有使用过面向方面的编程方法。
【解决方案3】:

我认为在项目中改造我的解决方案并不容易,但我这样做是这样的:

当我希望在我的应用程序中将故障识别为关键故障时,我会使用异常。在我的错误处理程序中,我有一些逻辑可以确定异常是否与 4xx 之类的 http 状态代码匹配,如果不是生成通用 500 状态代码。

非关键故障由使用自定义结果类的破坏性较小的机制捕获。它的基本结构类似于异常,因为codemessage 作为构造函数参数传递。此外,结果对象可以作为可选参数传递。

Result 类如下所示:

class Result
{
    /**
     * @var int
     */
    protected $resultCode;

    /**
     * @var string
     */
    protected $resultMessage;

    /**
     * @var mixed
     */
    protected $resultObject;

    /**
     * Inject object values on construction.
     *
     * @param int $code
     * @param string $message
     * @param mixed $result
     */
    public function __construct($code = 0, $message = null, $result = null)
    {
        $this->resultCode = intval($code);
        $this->resultMessage = (string) $message;
        $this->resultObject = $result;
    }

    /**
     * Returns true when result is considered a success, determined by the
     * resultCode, otherwise false.
     *
     * @return bool
     */
    public function isSuccess()
    {
        if ($this->resultCode > 0) {
            return true;
        }
        return false;
    }


    /**
     * Returns true when result is considered an error, determined by the
     * resultCode, otherwise false.
     *
     * @return bool
     */
    public function isError()
    {
        if ($this->resultCode < 1) {
            return true;
        }
        return false;
    }

    /**
     * Returns the result message.
     *
     * @return string
     */
    public function getMessage()
    {
        return $this->resultMessage;
    }

    /**
     * Returns the actual result object.
     *
     * @return mixed
     */
    public function getResult()
    {
        return $this->resultObject;
    }
}

只需要结果代码。如果代码> 0则认为结果是成功的,否则认为是错误的。结果对象可以是实际结果(如果成功),例如来自查询的行或行集,或者(如果错误)输入或输入的一部分,它们是相关的,例如您要查找的行 ID。

由于这个结果类不会触发 errorHandler,因此 MVC 应用程序被信任来处理错误。通过抛出异常,或者通过显示消息或作为结果对象传递的任何内容来优雅地对失败结果做出反应。

如果我将此结果传递给我的视图,我可以对$result-&gt;isSuccess() 是真还是假做出反应,并通过$result-&gt;getResult() 获得实际结果或通过$result-&gt;getMessage() 显示错误消息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-01-09
    • 2011-05-07
    • 2018-02-02
    • 1970-01-01
    • 2021-08-23
    • 2010-10-12
    • 2014-08-12
    相关资源
    最近更新 更多