最近这几天在公司SDK接口项目使用到在插入数据库之前对其他平台的数据进行数据上报。
上报需要访问别的服务,比较耗时,会影响到正常的数据插入。因此用到了队列。
收获:这次自己独立完成队列让我更明白compser的自动加载原理,以及PHP进程的相关内容、
队列说白了就是Job、Queue、Worker;其中Job负责处理对应事件的逻辑,Queue用于接收队列消息,Worker常驻内存,循环POP队列中的服务。
就一个while(true)循环,然后其中实现fork子进程来执行任务的过程
PHP队列有多种实现方式
1. PHP-redis 自己做消息队列
2. PHP自带扩展 gearman,和OMQ消息系统(这两个没研究过,但是看文档里面有,有时间研究研究)
3. MemcacheQ
4. RabbitMQ
5. PHP-resque
之前公司用过RabbitMQ,自己掌握的不怎么好。这次选型的时候,首相考虑到用redis和php来实现队列、
最后看php-reques 用的人蛮多,然后就选了这个。
注意:
- php-resque 需要 PCNTL 函数的支持。需要加入PHP的PCNTL的扩展。
- 需要有redis,php-resque不支持有密码验证的redis,需要自己来实现下功能
- PHP-resque 没有使用命名空间,引用的时候用的是顶级命名空间。
安装:
composer.json写入:
"require": {
"chrisboulton/php-resque": "1.2.x"
},
执行
composer update
Tip:可以看到这里autoload 使用的psr-0 的协议。然后指向了lib这个文件夹,在写woker的时候有个地方一直不明白。待会贴出代码。
-
使用
tip:可以参照 chrisboulton/php-resque/demo 下面的文件
* 如何使用
* https://packagist.org/packages/chrisboulton/php-resque
* https://icewing.cc/post/background-jobs-and-phpresque-5.html
* http://avnpc.com/pages/run-background-task-by-php-resque#toc8
*
* 首先启动守护队列:
* QUEUE=default VVERBOSE=1 php demo/resque.php
* 前面的QUEUE部分是设置环境变量,我们指定当前的Worker只负责处理default队列。也可以使用QUEUE=* php demo/resque.php 来处理所有队列
* QUEUE: 需要执行的队列的名字
* INTERVAL:在队列中循环的间隔时间,即完成一个任务后的等待时间,默认是5秒
* APP_INCLUDE:需要自动载入 PHP 文件路径,Worker 需要知道你的 Job 的位置并载入 Job
* COUNT:需要创建的 Worker 的数量。所有的 Worker 都具有相同的属性。默认是创建1个Worker
* REDIS_BACKEND:Redis 服务器的地址,使用 hostname:port 的格式,如 127.0.0.1:6379,或 localhost:6379。默认是 localhost:6379
* REDIS_BACKEND_DB:使用的 Redis 数据库的名称,默认是 0
* VERBOSE:啰嗦模式,设置 1 为启用,会输出基本的调试信息
* VVERBOSE:设置“1”启用更啰嗦模式,会输出详细的调试信息
* PREFIX:前缀。在 Redis 数据库中为队列的 KEY 添加前缀,以方便多个 Worker 运行在同一个Redis 数据库中方便区分。默认为空
* PIDFILE:手动指定 PID 文件的位置,适用于单 Worker 运行方式
* 以上参数中只有QUEUE是必须的。如果让 Worker 监视执行多个队列,可以用逗号隔开多个队列的名称,如:queue1,queue2,queue3,队列执行是有顺序的,如上 queue2 和 queue3 总是会在 queue1 后面被执行。
* 也可以设置QUEUE为*让 Worker 以字母顺序执行所有的队列。
*
* 结束进程
* kill -QUIT YOUR-WORKER-PID
* QUIT - 等待子进程结束后再结束
* TERM / INT - 立即结束子进程并退出
* USR1 - 立即结束子进程,但不退出
* USR2 - 暂停Worker,不会再执行新任务
* CONT - 继续运行Worker
*
* 记录下 Worker 的输出
\\192.168.2.142\share\XXX\vendor\chrisboulton\php-resque\demo\resque.php
* nohup QUEUE=notification VVERBOSE=1 INTERVAL=10 COUNT=5 APP_INCLUDE=/usb/html/localapi/sdkapi/bin/loader.php REDIS_BACKENT=192.168.2.142:6379 php /usb/html/localapi/sdkapi/bin/daemo_queue.php >> /var/log/phpresque/infofile.log 2>&1 &
* nohup 表示后台运行守护进程
*
* Resque_Job_Status::STATUS_WAITING = 1; (等待)
* Resque_Job_Status::STATUS_RUNNING = 2; (正在执行)
* Resque_Job_Status::STATUS_FAILED = 3; (失败)
* Resque_Job_Status::STATUS_COMPLETE = 4; (结束)
* 移除Jobs
* Removes multiple jobs
* Resque::dequeue('default');
* Resque::dequeue('default', ['My_Job']);
* Resque::dequeue('default', ['My_Job' => '087df5819a790ac666c9608e2234b21e']);
* Resque::dequeue('default', ['My_Job' => array('foo' => 1, 'bar' => 2)]);
* Resque::dequeue('default', ['My_Job', 'My_Job2']); 移除多个jobs
*
*
*
* 消费者可以有三个方法 worker
* public function setUp() {} // .. Set up environment for this job
* public function perform() {} // .. Run job
* public function tearDown() {} // ... Remove environment for this job
*
* 生产者
* Resque::setBackend('127.0.0.1:6379');
* $args = array(
* 'time' => time(),
* 'array' => array(
* 'test' => 'test',
* ),
* );
* $jobId = Resque::enqueue('default', $argv[1], $args, true);
* $argv[1]为调用的类, $args 为参数
* 最后一行的第一个参数表示 消息队列的名称(可随意标记,比如 email,log等),第二个参数表示取出任务后,由My_Job这个类来处理此条任务
*
*
* http://www.jianshu.com/p/395652dc66f1
* 监控resque-web
* gem install resque-web -v 0.0.8
* 运行resque-web -p 40000
* /usr/bin/python /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
*
*
* 总结:
* 开启守护队列 QUEUE=* php resque.php >> /var/log/phpresque/logfile.log &
* 调用job php queue.php PHP_Job
* 执行worker PHP_Job->perform() 在 tail -f /var/log/phpresque/logfile.log 可以看到执行的结果
*/
代码:
worker常驻进程:
JOBS: 这里用这个命名空间需要在composer中加入 JOBS 这个目录
Queue:
插入队列
使用上面的代码:启动脚本,第二个参数使用命名空间才能导入自动加载。很多案例里面没有使用到命名空间,这里可能会有问题。
php /usb/html/XXX/queue_resident.php >> /var/log/phpresque/infofile.log 2>&1
关于composer自动加载的,可以参照别人的文章,好好阅读下,我这边感觉也没有特别的要总结的~~就不写啦