12

我现在使用php-activerecord有一段时间了,我非常喜欢它。php-activerecord 是一个基于ActiveRecord 模式的开源 ORM 库。但是,我最近尝试将它与基于Wrench的 websocket 应用程序结合使用。

这非常有效,但是要启动脚本,应用程序必须在 linux 上作为守护进程运行,以使 websocket 始终可用。在一段时间不使用该应用程序然后尝试再次使用它之后,它会引发一些数据库异常:

起初它会发出警告:

PHP Warning: Error while sending QUERY packet. PID=XXXXX in /home/user/domains/example.com/public_html/vendor/php-activerecord/php-activerecord/lib/Connection.php on line 322

然后它抛出一个致命错误:

PHP Fatal error: Uncaught exception 'ActiveRecord\DatabaseException' with message 'exception 'PDOException' with message 'SQLSTATE[HY000]: General error: 2006 MySQL server has gone away' in /home/user/domains/example.com/public_html/vendor/php-activerecord/php-activerecord/lib/Connection.php:322

堆栈跟踪:

#0 /home/user/domains/example.com/public_html/vendor/php-activerecord/php-activerecord/lib/Connection.php(322): PDOStatement->execute(Array)

#1 /home/user/domains/example.com/public_html/vendor/php-activerecord/php-activerecord/lib/Table.php(218): ActiveRecord\Connection->query('SELECT * FROM ...', Array)

#2 /home/user/domains/example.com/public_html/vendor/php-activerecord/php-activerecord/lib/Table.php(209): ActiveRecord\Table->find_by_sql('SELECT * FROM `...', Array, false, NULL)

#3 /home/user/domains/example.com/public_html/vendor/php-activerecord/php-activerecord/lib/Model.php(1567): ActiveRecord\Table->find(Array)

#4 in /home/user/domains/example.com/public_html/vendor/php-activerecord/lib/Connection.php on line 325

似乎 php-activerecord 在 websocket 服务器运行时一直保持 mysql 连接打开,如果它随后自动尝试重新连接并再次运行查询,这当然不是问题。但事实并非如此。

我读过一些关于设置的东西MYSQL_OPT_RECONNECT。但我不确定这是否有效或如何使用 php-activerecord 设置该选项。这里有人有这方面的经验吗?

编辑:这是我的全局超时配置变量

VARIABLE_NAME                   VARIABLE_VALUE  
DELAYED_INSERT_TIMEOUT          300
WAIT_TIMEOUT                    28800
CONNECT_TIMEOUT                 10
LOCK_WAIT_TIMEOUT               31536000
INNODB_ROLLBACK_ON_TIMEOUT      OFF
THREAD_POOL_IDLE_TIMEOUT        60
NET_WRITE_TIMEOUT               60
INNODB_LOCK_WAIT_TIMEOUT        50
INTERACTIVE_TIMEOUT             28800
DEADLOCK_TIMEOUT_LONG           50000000
SLAVE_NET_TIMEOUT               3600
DEADLOCK_TIMEOUT_SHORT          10000
NET_READ_TIMEOUT                30
4

6 回答 6

5

PHP ActiveRecord 使用 PDO。绝对没有办法关闭 PDO 连接,对于长时间运行的后台任务来说,这是错误的 DB 层。

您可以尝试使用以下代码段来影响PDO 连接的断开。

//if not using ZF2 libraries, disconnect in some other way
$db->getDriver()->getConnection()->disconnect()
$db = NULL;
gc_collect_cycles();

断开连接,将引用设置为 null,然后运行垃圾收集器。希望这将调用 PDO 的内部 __destruct 方法来实际关闭连接。

必须在您自己的长时间运行的脚本中管理您的数据库连接。如果您的工作人员有一段时间不需要处理工作,您必须断开连接,并且在您有工作时必须重新连接。

真正的解决方案是不使用PDO,正常断开重连。

如果您只是将服务器和客户端库的超时设置为无限,您将遇到永远不会死机的失控脚本问题,迫使您重新启动整个服务器(搞砸超时不是一个好主意)。

编辑:我实际上遇到了这个确切的问题,并且去年在工作中使用了这个确切的解决方案。这解决了我 99% 的问题。但是,每隔一段时间就会出现一个我无法捕获并尝试重新连接的杂散连接异常。我只是每天重新启动一次进程,以摆脱那些杂散的连接错误。这就是为什么我的回答是,不要使用 PDO。立即切换并真正控制断开连接和重新连接。

于 2015-04-22T13:25:34.720 回答
1

MySQL server has gone away 错误最常见的原因是服务器超时并关闭了连接。

尝试进行以下更改。

max_allowed_packet=64M

如果您有很多请求,请设置它并且不要将其设置为更大,因为它与您的环境有关。

max_connections=1000

将此行添加到my.cnf文件中可能会解决您的问题。完成更改后重新启动 MySQL 服务。

阅读有关MySQL 服务器的更多信息已消失

如果它不起作用,请尝试使用自动重新连接功能。

于 2015-04-17T09:20:30.290 回答
1

如果您的数据库未处理多个并发连接和查询,您可以设置“无限”超时。这不会显着影响数据库资源。最好的方法是发送 ping 数据包(选择 1)来更新超时并使连接保持活动状态。

于 2015-04-22T20:35:18.460 回答
1

它也可能是查询的大小,因为有时 ORM 会组合查询以提高性能。

尝试设置 max_allowed_pa​​cket=128M,至少应该作为诊断有用。

于 2015-04-21T15:23:20.027 回答
1

如前所述,PHP 脚本中的 MySQL 在一段时间内两者之间没有通信时会超时。这是一件好事,因为空闲连接会占用您的服务器资源。

“服务器已消失”错误主要发生在两个查询之间发生相对冗长的计算时。

为了防止这种情况,您可以

  • 在执行期间定期执行SELECT 1查询
  • 围绕您的查询创建一个包装器,在执行之前检查连接是否有效
  • 使用这篇文章的答案

但是,我相信重新配置 MySQL 以保持连接打开更长时间会鼓励粗心的编程,并建议反对它。

于 2015-04-17T14:18:35.333 回答
0

为了解决这样的问题,我建议你:

  1. 使用 Gearman 作业服务器 ( http://gearman.org/ )分发您的流程
  2. 使用 Supervisor ( http://supervisord.org/ )轻松管理这些流程

这里是如何。

将您的 Web 套接字应用程序作为守护程序运行,就像您现在所做的一样(可能使用 cron)。或者更好的是,使用 Supervisor 管理它。配置它,以便在 Supervisor 启动时 Supervisor 启动它,如果守护进程死亡则自动重新启动它。

示例配置:

[program:my-daemon]
command=/usr/bin/php /path/to/your/daemon/script
autostart=true
autorestart=true

接下来,不要在应用程序守护进程中运行查询处理,而是创建一个 Gearman Worker 来处理它。注册后,Worker 将等待运行/调用。您必须从您的 websocket 应用程序中调用 Worker,并在必要时连同必要的工作负载参数一起调用(有关此工作负载术语解释,请参阅 Gearman 网站)。

在 Worker 中,将其设置为在它已经完成守护进程请求的作业时停止/退出。这样,您将不会遇到“mysql 服务器已消失问题”,因为连接会立即关闭。

最后,我们必须像守护进程一样让 Worker 始终可用。因此,与守护进程类似,将 Supervisor 配置为在 Worker 死亡/停止时自动启动和自动重启,如下所示:

[program:my-worker]
command=/usr/bin/php /path/to/your/worker/script
autostart=true
autorestart=true

另一个有趣的事情是,您可以添加任意数量的 Worker 来等待。只需添加以下配置:

numprocs=9 #change it to any number
process_name=%(program_name)s_%(process_num)02d #to identity worker number

由于我们告诉 Supervisor 自动重启每个进程,所以我们总是有恒定的 Worker 在后台运行。

这是有关此策略的另一种解释:http: //www.masnun.com/2011/11/02/gearman-php-and-supervisor-processing-background-jobs-with-sanity.html

希望有帮助!

于 2015-04-20T15:58:37.770 回答