前言
由于本人个人原因不太喜欢使用Linux的Crontab或者使用Supervisor去执行php的一些异步进程。
有了workerman的加持,我们可以使用workerman的一些方法特性去实现纯php版的异步功能。
因为workerman这个框架自身也是纯PHP实现的高性能异步PHP socket即时通讯框架,php加持php岂不美哉?
(PS:workerman死忠粉)
准备工作
Laravel版本:6.0
Workerman版本:4.0
workerman的运行环境需要安装pcntl和posix扩展。
我这里使用的是laravel的集成docker环境 laradock
是可以直接使用的
Workerman环境具体可以访问:http://doc.workerman.net/install/install.html
Laradock环境https://laradock.io/
具体实现
一、安装workerman
composer require workerman/workerman
二、编写Artisan控制台命令方便调用workerman
php artisan make:command WorkermanTimerCommand
可以看到在laravel的app\Console\Commands目录下生产了一个WorkermanTimerCommand.php文件。
三、接着我们开始编写workerman的控制台命令支持
<?php
namespace App\Console\Commands;
use App\Listens\WorkermanTimers;
use Illuminate\Console\Command;
use Workerman\Worker;
class WorkermanTimerCommand extends Command
{
/**
* 控制台命令的名称和参数.
*
* @var string
*/
protected $signature = 'workerman {action} {--d}';
/**
* 控制台命令描述.
*
* @var string
*/
protected $description = 'workerman的多进程定时任务';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
global $argv;
$action = $this->argument('action');
$argv[0] = 'wk';
$argv[1] = $action;
$argv[2] = $this->option('d') ? '-d' : '';
$this->startServer();
}
/**
* 启动workerman服务
*/
public function startServer()
{
$worker = new Worker();
// 服务名称.
$worker->name = 'laravel timer';
// 启动多少个进程数量,这里大家灵活配置,可以参考workerman的文档.
$worker->count = 4;
// 当workerman的进程启动时的回调方法.
$worker->onWorkerStart = [WorkermanTimers::class, 'onWorkerStart'];
// 当workerman的进程关闭时的回调方法.
$worker->onClose = [WorkermanTimers::class, 'onClose'];
Worker::runAll();
}
}
可以看到在上面的startServer
方法里,我们为onWorkerStart
和onClose
分别注册回调到了WorkermanTimers
类里面的onWorkerStart
和onWorkerStart
方法。
这里的意思是当workerman里面的进程启动的时候,会调用WorkermanTimers
类里面对应的方法!
那我们来看看这个WorkermanTimers
类究竟需要干一些什么!
四、编写定时器任务分发类(WorkermanTimers)
我们现在app目录下新建一个Listens
目录,并创建WorkermanTimers
类
WorkermanTimers
类具体代码:
namespace App\Listens;
use Workerman\Lib\Timer;
class WorkermanTimers
{
/**
* 服务进程启动时
* @param $businessWorker
*/
public static function onWorkerStart($businessWorker)
{
// 拿到当前进程的id编号.
$workid = $businessWorker->id;
// 获取所有定时器任务配置.
$timedTask = config('timers');
if (is_array($timedTask)) {
// 循环检测任务绑定.
foreach ($timedTask as $key => $value) {
// 绑定任务进程.
if ($value['worker_id'] == $workid) {
Timer::add($value['time'], $value['func']);
}
}
}
}
/**
* 服务进程结束时
* @param $client_id
*/
public static function onClose($client_id)
{
}
}
解释:
以上onWorkerStart
方法就是workerman每个进程刚启动的时候
会回调过来当前进程的属性$businessWorker
我们在WorkermanTimerCommand
里面定义的进程数count=4
,就是4个进程。也就是说onWorkerStart
方法会被调用四次,因为多进程里每个进程相互隔离,所以每次我们拿到的$businessWorker->id
都会不一样。
worker进程的id编号,范围为0到$worker->count-1,所以每次$businessWorker->id
编号应该是0,1,2,3
所以我们循环定义的timers
配置,判断每个配置绑定的进程编号如果和当前的进程编号一致,我们就为当前进程增加一个定时器!
可能各位看官有疑问了,timers
的配置在哪呢?
我们接着往下走
五、在config/
目录下新增一个timers.php
具体配置如下:
<?php
return [
// 定时任务名称.
'say' => [
'worker_id' => 1, // 需要绑定的进程id.
'time' => 5, // 时间间隔 秒为单位.
'func' => 'Facades\App\Services\TestService::testSay', // 定时执行的方法.
],
/* 'eat' => [
'worker_id' => 2, // 需要绑定的进程id.
'time' => 10, // 时间间隔 秒为单位.
'func' => 'Facades\App\Services\TestService::testEat', // 定时执行的方法.
]*/
];
timers.php
返回的是一个多维数据结构的配置,
数组一级元素代表这个定时任务的名称(为了区分和理解),
数组二级元素worker_id
代表我们需要绑定到哪个进程
数组二级元素time
表示时间间隔,多少秒执行一次
数组二级元素func
代表要执行哪个方法
在结合上面的WorkermanTimers
类去看,就可以理解为:
任务:say
绑定在进程编号为1
的worker上面,每5秒就会执行一次\App\Services\TestService
类里面的testSay
方法
在laravel里面,命名空间前面加上Facades\
就可以静态调用该类的方法,这部分的概念大家可以去看看laravel文档
六、编写定时任务类
我们在app
目录下新建一个Services
文件夹,并且新增一个TestService
类
TestService
类具体代码:
<?php
namespace App\Services;
class TestService
{
public function testSay()
{
var_dump("Hello Laravel and Workerman");
}
}
七、测试
输入命令php artison workerman start
启动workerman
看看有没有任务执行:
命令:
启动:
php artisan workerman start
启动常驻内存:
php artisan workerman start --d
其他命令:
php artisan workerman reload //重新加载配置
php artisan workerman reload //重启workerman
php artisan workerman stop //停止workerman
总结
流程:
1.当workerman的每个进程启动后,会回调给WorkermanTimers
类的onWorkerStart
方法
2.onWorkerStart
会加载所有定时任务的配置,然后去循环绑定任务类
3.添加workerman定时间并绑定任务类方法!
4.每个进程绑定一个定时任务,当然也可以一个进程绑定多个定时器任务,具体取决于你怎么在timers.php
里面配置
参考文档
其他
workerman是一款底层设计非常优秀的框架,纯php实现高性能常驻内存特性。
workerman能做的事情也远远不止于此,还有很多例如:IM、游戏、物联网等等功能等你去探索!
当优雅的Laravel
遇上 性能强悍得 Workerman
会发生点什么呢?
当结束worker man时会自动销毁定时吗