假设您正在编写一个为作业队列提供服务的守护进程。各种其他软件将守护进程的作业写入队列。守护进程每隔几秒轮询一次队列以查找挂起的作业。假设队列被实现为 MySQL 数据库中的一个表,并且守护进程是一个简单的循环:
- 从队列中获取所有到期的工作
- 做作业
- 休眠N秒
- 转到1
守护进程必须在 MySQL 数据库服务器中断的服务和数据库连接中断时存活下来。
您是否会将守护程序设计为每个周期连接一次数据库服务器?即在 1 之前连接并在 2 和 3 之间断开连接?
或者您会让守护进程保持连接打开吗?在这种情况下,它还需要 a) 检测服务器或连接何时不工作,b) 断开连接并重新连接,c) 在不累积数据库连接、无效连接描述符或其他死资源的情况下这样做。
如果您有偏好,为什么?
优缺点?
进入设计的因素?
还有其他方法吗?
答案在这里:mysql connection from daemon written in php没有说明为什么最好保持连接打开。我在别处读到,MySQL 中的每个连接开销非常小。因此,为什么永久使用一个服务器连接比每隔几秒连接/断开连接要好,这一点并不明显。
在我的例子中,守护进程是用 PHP 编写的。
我实际上正在研究 something与您描述的非常接近,但在我的例子中,守护进程不会轮询它通过 XMPP 异步获取的事件(但这不是重点)。
去掉中间人
我认为与其将事件存储在数据库中并使用 MySQL 轮询它们,不如使用 Gearman。从客户端异步发送它们(example)。
垃圾回收
PHP 并非真正设计为作为守护进程运行,直到 PHP 5.3 才获得 circular reference garbage collection。它成为一个可行的选择。如果您想长期运行而不会出现内存泄漏,那么使用 PHP 5.3 是非常重要的。
另一件关于 GC 需要牢记的事情是,内存只有在不再被引用(任何地方)时才是空闲的。因此,如果您将变量分配给全局范围,它将一直存在直到守护进程退出。重要的是,您创建或使用的任何代码都不会在某些地方建立变量(即静态日志、不删除旧数据等)。
统计缓存
另一件事是,经常运行 clearstatcache
很重要。由于您的 PHP 进程未重新启动,因此手动执行此调用非常重要,以防止获取旧的统计数据(这可能会或可能不会影响您)。根据documentation这些函数被缓存。
Affected functions include stat(), lstat(), file_exists(), is_writable(), is_readable(), is_executable(), is_file(), is_dir(), is_link(), filectime(), fileatime(), filemtime(), fileinode(), filegroup(), fileowner(), filesize(), filetype(), and fileperms().
资源管理
如果您打算在进程的生命周期内使用 MySQL 之类的东西,我建议在启动时建立一个连接并保持连接。尽管它可能会在 MySQL 端使用更多内存,但您不必每 1 秒连接一次,从而减少一些延迟和 CPU 开销。
没有 URL 请求
这看起来很明显,但是 CLI PHP 没有 URL 请求信息。有些库在编写时并未考虑到这一点,这可能会导致一些问题。
LooPHP
我要在这里插入一个无耻的插件,用于我编写的框架,以帮助管理 PHP 守护进程。 LooPHP是一个运行循环框架,可让您安排事件发生或创建抽象源(套接字、流等)的监听。在我的例子中,我让守护进程做不止一件事,所以让系统为我跟踪所有计时器非常有帮助,这样我就可以有效地为 XMPP 连接轮询 stream_select
。