Phalcon+Beanstalkd:如何指定控制器执行后台任务?
嘿,我来帮你搞定Phalcon结合Beanstalkd处理后台邮件任务的配置~ 咱们分步骤来,覆盖你提到的两个核心文件,还有必要的Worker脚本:
1. 在Services.php中注册Beanstalkd服务
首先要在DI容器里把Beanstalkd服务配置好,这样控制器和Worker都能方便地获取到队列实例。打开你的Services.php,添加以下代码:
use Phalcon\Queue\Beanstalk; $di->setShared('beanstalk', function () { $beanstalk = new Beanstalk([ 'host' => '127.0.0.1', // Beanstalkd服务器地址,默认本地 'port' => 11300, // 默认端口 'tube' => 'email_tube' // 给邮件任务单独指定一个管道,方便管理 ]); // 确保连接成功 if (!$beanstalk->connect()) { throw new \Exception('Failed to connect to Beanstalkd server'); } return $beanstalk; });
这里咱们给邮件任务单独开了个email_tube管道,避免和其他任务混在一起,后续Worker也会监听这个管道。
2. 修改MailController.php:投递任务+抽离核心逻辑
首先,你需要把原来在mailAction里的耗时IMAP操作抽成一个独立的方法(比如fetchEmailsFromImap),然后mailAction只负责把任务投递到队列,而不是直接执行:
use Phalcon\Mvc\Controller; class MailController extends Controller { public function mailAction() { // 这里可以先做一些参数校验、权限检查等前置操作 $imapConfig = [ 'host' => 'your-imap-host.com', 'port' => 993, 'username' => 'your-email@domain.com', 'password' => 'your-password' // 其他IMAP配置项 ]; // 把任务数据投递到Beanstalkd队列 $this->beanstalk->put([ 'action' => 'fetchEmails', 'config' => $imapConfig ]); // 返回API响应,告诉用户任务已启动 $this->response->setJsonContent([ 'status' => 'success', 'message' => 'Email fetch task has been queued' ])->send(); } /** * 实际执行IMAP获取邮件的耗时方法 * 注意:这个方法要能独立运行,不要依赖控制器的请求上下文 */ public function fetchEmailsFromImap($imapConfig) { // 这里写你的IMAP连接、获取邮件、处理邮件的逻辑 // 比如: $imapStream = imap_open( "{".$imapConfig['host'].":".$imapConfig['port']."/imap/ssl}", $imapConfig['username'], $imapConfig['password'] ); if (!$imapStream) { // 处理连接错误,比如记录日志 error_log('IMAP connection failed: ' . imap_last_error()); return; } // 执行获取邮件的操作... $emails = imap_search($imapStream, 'ALL'); // 处理邮件... imap_close($imapStream); } }
这里要注意:fetchEmailsFromImap方法必须能脱离请求上下文独立运行,不能依赖$this->request这类和HTTP请求相关的对象,所有需要的参数都通过传入的$imapConfig来获取。
3. 编写Beanstalkd Worker脚本,监听并执行任务
Beanstalkd需要单独的Worker进程来监听队列,取出任务后执行对应的方法。咱们新建一个worker.php文件(放在项目根目录或者专门的脚本目录):
<?php // 加载Phalcon的初始化文件(比如你的public/index.php里的初始化逻辑,或者单独的bootstrap文件) require_once __DIR__ . '/app/config/bootstrap.php'; // 获取DI容器 $di = \Phalcon\Di\FactoryDefault::getDefault(); // 获取Beanstalkd服务,切换到指定的管道 $beanstalk = $di->getShared('beanstalk'); $beanstalk->watch('email_tube'); $beanstalk->ignore('default'); // 忽略默认管道,只监听咱们的邮件管道 echo "Worker started, listening on 'email_tube'...\n"; while (true) { // 从队列中取出任务 $job = $beanstalk->reserve(); if ($job) { try { // 解析任务数据 $taskData = json_decode($job->getData(), true); if ($taskData['action'] === 'fetchEmails') { // 实例化MailController,调用耗时方法 $mailController = new MailController(); $mailController->fetchEmailsFromImap($taskData['config']); // 任务执行成功,删除队列中的任务 $beanstalk->delete($job); echo "Task completed and deleted\n"; } else { // 未知任务类型,标记为失败 $beanstalk->bury($job); echo "Unknown task action, job buried\n"; } } catch (\Exception $e) { // 任务执行出错,记录日志,然后可以选择重新放回队列或者标记失败 error_log('Task failed: ' . $e->getMessage()); // 比如延迟10秒后重新尝试 $beanstalk->release($job, 0, 10); echo "Task failed, released for retry\n"; } } // 可以加个短暂休眠,避免CPU占用过高 usleep(100000); }
4. 启动Worker并测试
最后,在终端里启动Worker进程:
php worker.php
如果要让Worker在后台守护运行,可以用nohup或者supervisor来管理(推荐用supervisor,方便监控和自动重启):
nohup php worker.php > worker.log 2>&1 &
现在,当你调用mailAction的路由时,任务会被投递到Beanstalkd队列,Worker会自动取出并执行fetchEmailsFromImap方法,API响应会立即返回,不会等待耗时操作完成~
内容的提问来源于stack exchange,提问作者Finkle

