有一個動態配置數組, 每一個數組元素定時啓動任務. 如何實現?php
源碼基於 laravel 5.5.45.laravel
app/Console/Kernel.phpwindows
class Kernel extends ConsoleKernel
protected function schedule(Schedule $schedule)
{
$schedule->command("test_b", ['rule-content'])->runInBackground()->withoutOverlapping()->sendOutputTo(storage_path('logs/xx.log'));
}
}
複製代碼
crontab 配置每分鐘調用檢測數組
* * * * * php /path/to/artisan schedule:run
複製代碼
上面是一個普通定時任務的一種寫法, 固然咱們這是是根據配置 動態的執行任務.安全
laravel 能夠解析成任務、又能夠執行任務, 咱們能不能基於它來實現呢bash
vendor/laravel/framework/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php
複製代碼
這個定時任務是laravel直接提供的, 做爲咱們 (schedule:run) 定時任務的執行文件。 和咱們自建的任務類沒什麼區別 他是每分鐘執行檢測是須要執行到期的任務.php7
咱們看下這個文件的 handle 實現app
public function handle()
{
$eventsRan = false;
foreach ($this->schedule->dueEvents($this->laravel) as $event) {
if (! $event->filtersPass($this->laravel)) {
continue;
}
$event->run($this->laravel);
$eventsRan = true;
}
...
}
複製代碼
最核心的是任務運行起來 執行了 event 類的 run 方法ui
咱們前提是 咱們如何生成 event 對象, 咱們先從命令聲明開始this
$schedule->command('inspire');
複製代碼
這個是咱們定時任務的寫法, 咱們看下 Schedule 類的 command 方法.
public function command($command, array $parameters = [])
{
if (class_exists($command)) {
$command = Container::getInstance()->make($command)->getName();
}
return $this->exec(
Application::formatCommandString($command), $parameters
);
}
複製代碼
傳入咱們的 spire 命令, 調用 exec 執行命令.
在執行裏面, 調用了 Application::formatCommandString 返回了咱們想要的命令基本雛形.
'/usr/local/php7/bin/php' 'artisan' inspire
複製代碼
調用的exec方法實現:
public function exec($command, array $parameters = [])
{
if (count($parameters)) {
$command .= ' '.$this->compileParameters($parameters);
}
$this->events[] = $event = new Event($this->mutex, $command);
return $event;
}
複製代碼
若是存在參數的話, 調用 compileParameters 對參數進行安全處理並返回回來, 拼接到咱們的執行命令後面, 而後咱們發現將命令傳入 event, 並 return 了 event 對象.
固然針對 event 類的方法還有不少, 好比咱們使用 withoutOverlapping 方法上鎖, 防止任務超時再次執行.
咱們將任務放到後臺執行, 防止影響下面的任務執行, 可使用 runInBackground
完整示例
$schedule->command("test_b", ['rule-content'])->runInBackground()->withoutOverlapping()
複製代碼
具體 runInBackground 和 withoutOverlapping 實現方式請往下看.
public function run(Container $container)
{
if ($this->withoutOverlapping &&
! $this->mutex->create($this)) {
return;
}
$this->runInBackground
? $this->runCommandInBackground($container)
: $this->runCommandInForeground($container);
}
複製代碼
咱們能夠看到剛纔咱們提到的關於 withoutOverlapping 和 runInBackground 的兩個邏輯斷定
以上源碼實現也很簡單, 感興趣能夠研究看看
run 方法調用 runCommandInBackground 後臺運行任務實現
protected function runCommandInBackground(Container $container)
{
$this->callBeforeCallbacks($container);
(new Process(
$this->buildCommand(), base_path(), null, null, null
))->run();
}
複製代碼
這裏使用了 syfomy 的 process類, 建立一個新進程. 咱們再也不深究 process的實現, 咱們來看 buildCommand
protected function buildBackgroundCommand(Event $event)
{
$output = ProcessUtils::escapeArgument($event->output);
$redirect = $event->shouldAppendOutput ? ' >> ' : ' > ';
$finished = Application::formatCommandString('schedule:finish').' "'.$event->mutexName().'"';
return $this->ensureCorrectUser($event,
'('.$event->command.$redirect.$output.' 2>&1 '.(windows_os() ? '&' : ';').' '.$finished.') > '
.ProcessUtils::escapeArgument($event->getDefaultOutput()).' 2>&1 &'
);
}
複製代碼
這是生成後臺運行進程任務的核心實現, 和另外一個前臺執行相比主要多了一個 & 號
咱們打印看看這是個什麼樣子的命令?
$sh = $schedule->command("test_b", ['rule-content'])->runInBackground()->withoutOverlapping()->buildCommand();
echo $sh;
複製代碼
最終命令輸出狀況
('/usr/local/php7/bin/php' 'artisan' test_b 'rule-content' > '/dev/null' 2>&1 ; '/usr/local/php7/bin/php' 'artisan' schedule:finish "framework/schedule-8d9802e101a46785c4a1222384c28652b39a03a6") > '/dev/null' 2>&1 &
複製代碼
由上可知, 咱們若是手動實現調用的話, 能夠直接調用 event 裏面的 run方法便可, 實現以下(不一樣版本實現不同, 但大概思路一致, 如下基於laravel 5.5)
$schedule = app(Schedule::class);
$event = $schedule->command("test_b", ['rule-content'])->runInBackground()->withoutOverlapping()->run($this->laravel);
複製代碼