Laravel 13 进程管理完全指南
Laravel 提供了强大的进程管理功能,包括 Artisan 命令、进程调度和外部进程调用。本文将深入探讨 Laravel 13 中进程管理的各种用法。
Artisan 命令
创建命令
1 2
| php artisan make:command SendEmails php artisan make:command ProcessUsers --command=users:process
|
命令结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| <?php
namespace App\Console\Commands;
use App\Models\User; use App\Services\MailService; use Illuminate\Console\Command;
class SendEmails extends Command { protected $signature = 'email:send {user : 用户ID或邮箱} {--queue : 是否使用队列发送} {--subject= : 邮件主题}';
protected $description = '发送邮件给指定用户';
public function handle(MailService $mailer): int { $user = $this->resolveUser($this->argument('user')); if (!$user) { $this->error('用户不存在'); return self::FAILURE; }
$subject = $this->option('subject') ?? '欢迎邮件';
if ($this->option('queue')) { $mailer->queue($user, $subject); $this->info('邮件已加入队列'); } else { $mailer->send($user, $subject); $this->info('邮件已发送'); }
return self::SUCCESS; }
protected function resolveUser($identifier): ?User { if (is_numeric($identifier)) { return User::find($identifier); } return User::where('email', $identifier)->first(); } }
|
命令签名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| protected $signature = 'user:create {name} {email}';
protected $signature = 'user:create {name} {email?}';
protected $signature = 'user:create {name} {email=default@example.com}';
protected $signature = 'user:notify {user*}';
protected $signature = 'email:send {user} {--queue}';
protected $signature = 'email:send {user} {--subject=}';
protected $signature = 'email:send {user} {--subject=欢迎}';
protected $signature = 'email:send {user} {--Q|queue}';
protected $signature = 'report:generate {type : 报告类型} {--start= : 开始日期} {--end= : 结束日期} {--format=pdf : 输出格式} {--queue : 使用队列处理} {--notify=* : 通知邮箱}';
|
输入输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| public function handle(): int { $name = $this->argument('name'); $arguments = $this->arguments();
$queue = $this->option('queue'); $options = $this->options();
$this->info('操作成功'); $this->error('操作失败'); $this->warn('警告信息'); $this->line('普通输出'); $this->comment('注释信息');
$this->table( ['ID', 'Name', 'Email'], User::all(['id', 'name', 'email'])->toArray() );
$users = User::all(); $bar = $this->output->createProgressBar($users->count());
foreach ($users as $user) { $this->processUser($user); $bar->advance(); }
$bar->finish(); $this->newLine();
return self::SUCCESS; }
|
交互式输入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public function handle(): int { $name = $this->ask('请输入用户名'); $password = $this->secret('请输入密码'); $confirm = $this->confirm('确定要继续吗?'); $choice = $this->choice( '请选择角色', ['admin', 'editor', 'user'], 'user' ); $multiSelect = $this->choice( '选择权限', ['read', 'write', 'delete'], null, null, true );
$anticipate = $this->anticipate('输入邮箱', [ 'admin@example.com', 'user@example.com', ]);
return self::SUCCESS; }
|
进程调度
定义调度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| use Illuminate\Support\Facades\Schedule; use App\Console\Commands\SendEmails;
class Kernel extends ConsoleKernel { protected function schedule(Schedule $schedule): void { $schedule->command('emails:send') ->daily() ->at('09:00');
$schedule->job(new ProcessPodcasts()) ->hourly();
$schedule->exec('node /path/to/script.js') ->daily();
$schedule->call(function () { User::where('expired', true)->delete(); })->daily(); } }
|
调度频率
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| $schedule->command('report:generate')->everyMinute(); $schedule->command('report:generate')->everyFiveMinutes(); $schedule->command('report:generate')->everyTenMinutes(); $schedule->command('report:generate')->everyFifteenMinutes(); $schedule->command('report:generate')->everyThirtyMinutes(); $schedule->command('report:generate')->hourly(); $schedule->command('report:generate')->hourlyAt(17); $schedule->command('report:generate')->daily(); $schedule->command('report:generate')->dailyAt('13:00'); $schedule->command('report:generate')->twiceDaily(1, 13); $schedule->command('report:generate')->weekly(); $schedule->command('report:generate')->weeklyOn(1, '8:00'); $schedule->command('report:generate')->monthly(); $schedule->command('report:generate')->monthlyOn(4, '15:00'); $schedule->command('report:generate')->twiceMonthly(1, 16); $schedule->command('report:generate')->quarterly(); $schedule->command('report:generate')->yearly();
$schedule->command('report:generate')->weekdays(); $schedule->command('report:generate')->weekends(); $schedule->command('report:generate')->mondays(); $schedule->command('report:generate')->tuesdays(); $schedule->command('report:generate')->wednesdays(); $schedule->command('report:generate')->thursdays(); $schedule->command('report:generate')->fridays(); $schedule->command('report:generate')->saturdays(); $schedule->command('report:generate')->sundays();
$schedule->command('report:generate')->cron('0 0 * * *');
|
调度约束
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| $schedule->command('report:generate') ->daily() ->when(function () { return date('N') <= 5; });
$schedule->command('report:generate') ->daily() ->skip(function () { return app()->isDownForMaintenance(); });
$schedule->command('report:generate') ->daily() ->environments(['production']);
$schedule->command('report:generate') ->daily() ->days([Schedule::MONDAY, Schedule::WEDNESDAY]);
|
防止重叠
1 2 3 4 5 6 7 8 9 10 11
| $schedule->command('report:generate') ->daily() ->withoutOverlapping(60);
$schedule->command('report:generate') ->daily() ->onOneServer();
$schedule->command('report:generate') ->daily() ->runInBackground();
|
维护模式
1 2 3 4 5 6 7 8 9
| $schedule->command('report:generate') ->daily() ->evenInMaintenanceMode();
$schedule->command('report:generate') ->daily() ->skip(function () { return app()->isDownForMaintenance(); });
|
任务钩子
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| $schedule->command('report:generate') ->daily() ->before(function () { }) ->after(function () { }) ->onSuccess(function () { }) ->onFailure(function () { });
|
任务输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| $schedule->command('report:generate') ->daily() ->sendOutputTo(storage_path('logs/report.log'));
$schedule->command('report:generate') ->daily() ->appendOutputTo(storage_path('logs/report.log'));
$schedule->command('report:generate') ->daily() ->emailOutputTo('admin@example.com');
$schedule->command('report:generate') ->daily() ->emailOutputOnFailure('admin@example.com');
|
进程执行
Process 组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| use Illuminate\Support\Facades\Process;
$result = Process::run('ls -la');
echo $result->output(); echo $result->errorOutput(); echo $result->exitCode();
if ($result->successful()) { }
if ($result->failed()) { }
|
实时输出
1 2 3
| Process::run('ls -la', function (string $type, string $output) { echo $output; });
|
异步进程
1 2 3 4 5 6 7 8
| $process = Process::start('npm run build');
while ($process->running()) { echo $process->latestOutput(); sleep(1); }
$result = $process->wait();
|
超时设置
1
| $result = Process::timeout(120)->run('long-running-command');
|
环境变量
1 2 3
| $result = Process::withEnvironment([ 'NODE_ENV' => 'production', ])->run('npm run build');
|
工作目录
1
| $result = Process::path('/path/to/project')->run('npm install');
|
并行进程
1 2 3 4 5 6 7 8 9 10 11
| use Illuminate\Process\Pool;
[$first, $second, $third] = Process::concurrently(function (Pool $pool) { $pool->path(__DIR__)->command('ls -la'); $pool->command('cat file.txt'); $pool->command('echo "Hello World"'); });
echo $first->output(); echo $second->output(); echo $third->output();
|
管道操作
1 2 3 4
| Process::pipe(function ($pipe) { $pipe->command('echo "Hello"'); $pipe->command('sed "s/Hello/Goodbye/"'); });
|
命令调用
从代码调用命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| use Illuminate\Support\Facades\Artisan;
Artisan::call('email:send', [ 'user' => 1, '--queue' => true, ]);
$output = Artisan::output();
Artisan::queue('email:send', [ 'user' => 1, ])->onQueue('commands');
|
命令实例调用
1 2 3
| use App\Console\Commands\SendEmails;
Artisan::call(new SendEmails(1, 'welcome'));
|
自定义命令示例
数据库备份命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| <?php
namespace App\Console\Commands;
use Illuminate\Console\Command; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Process;
class DatabaseBackup extends Command { protected $signature = 'db:backup {--database=mysql : 数据库连接} {--path= : 备份路径} {--compress : 是否压缩}';
protected $description = '备份数据库';
public function handle(): int { $connection = $this->option('database'); $config = config("database.connections.{$connection}");
$filename = sprintf( 'backup_%s_%s.sql', $connection, now()->format('Y-m-d_His') );
$path = $this->option('path') ?? storage_path('backups'); $filepath = "{$path}/{$filename}";
$this->info("开始备份数据库: {$connection}");
$command = sprintf( 'mysqldump -h%s -u%s -p%s %s > %s', $config['host'], $config['username'], $config['password'], $config['database'], $filepath );
$result = Process::run($command);
if ($result->failed()) { $this->error('备份失败: ' . $result->errorOutput()); return self::FAILURE; }
if ($this->option('compress')) { $this->info('压缩备份文件...'); Process::run("gzip {$filepath}"); $filepath .= '.gz'; }
$this->info("备份完成: {$filepath}"); return self::SUCCESS; } }
|
数据导入命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| <?php
namespace App\Console\Commands;
use App\Imports\UsersImport; use Illuminate\Console\Command; use Maatwebsite\Excel\Facades\Excel;
class ImportUsers extends Command { protected $signature = 'users:import {file : 导入文件路径} {--chunk=1000 : 分块大小} {--validate : 仅验证不导入}';
protected $description = '导入用户数据';
public function handle(): int { $file = $this->argument('file'); if (!file_exists($file)) { $this->error("文件不存在: {$file}"); return self::FAILURE; }
$import = new UsersImport( chunkSize: $this->option('chunk'), validateOnly: $this->option('validate') );
$this->info('开始导入用户数据...'); $bar = $this->output->createProgressBar(); $import->setProgressBar($bar);
try { Excel::import($import, $file); } catch (\Exception $e) { $this->error('导入失败: ' . $e->getMessage()); return self::FAILURE; }
$this->newLine(); $this->info("导入完成:"); $this->line(" 成功: {$import->successCount}"); $this->line(" 失败: {$import->failureCount}"); if ($import->failures->isNotEmpty()) { $this->warn('失败记录:'); foreach ($import->failures as $failure) { $this->line(" 行 {$failure->row()}: {$failure->errors()[0]}"); } }
return self::SUCCESS; } }
|
总结
Laravel 13 的进程管理提供了:
- 强大的 Artisan 命令系统
- 灵活的任务调度
- 现代化的进程执行组件
- 完善的输入输出控制
- 丰富的交互式功能
掌握进程管理是构建复杂命令行应用和自动化任务的基础。