ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Coolify 中的 Laravel Actions 队列模式:Job 入口点(dispatch 与 asJob)完整指南

Coolify 中的 Laravel Actions 队列模式:Job 入口点(dispatch 与 asJob)完整指南 Coolify 中的 Laravel Actions 队列模式Job 入口点dispatch 与 asJob完整指南【免费下载链接】coolifyAn open-source, self-hostable PaaS alternative to Vercel, Heroku Netlify that lets you easily deploy static sites, databases, full-stack applications and 280 one-click services on your own servers.项目地址: https://gitcode.com/GitHub_Trending/co/coolifyCoolify 是一个开源、可自托管的 PaaS 平台其 Laravel 应用通过lorisleiva/laravel-actions包当前锁定版本^2.10.2见 composer.json将大量业务操作封装为可复用的 Action 类。本篇基于仓库内置的参考文档 .agents/skills/laravel-actions/references/job.md 展开系统讲解 Action 的「Job 入口点」——即如何把一个 Action 通过 Laravel 队列异步/同步执行覆盖全部 dispatch 变体、Job 包装与链式编排、测试断言辅助方法以及JobDecorator提供的重试、唯一性、超时与失败处理钩子并结合 Coolify 仓库中 60 余个真实 Action 类的用法让你掌握在大型 PaaS 后端中设计可靠队列任务的方法。一、为什么 Coolify 用 Action 的 Job 入口点在 Coolify 中app/Actions目录下按领域组织Application、Server、Service、Database、Proxy、Docker、Shared等存在 60 多个使用了Lorisleiva\Actions\Concerns\AsActiontrait 的类。同一个 Action 类可以同时以「对象」「控制器」「Job」「监听器」「命令」等多种入口点运行Job 入口点就是其中负责队列执行的那一层。仓库配套的 skill 文档 .agents/skills/laravel-actions/SKILL.md 给出了项目级约定与 job.md 的「推荐模式」一致用Action::dispatch(...)发起异步执行队列相关的编排逻辑放在asJob(...)中可复用的业务逻辑放在handle(...)中项目中的 Job Action 经常额外定义队列生命周期方法与 job 属性用于控制重试、唯一性和时序。推荐的职责分层可以概括为use Lorisleiva\Actions\Concerns\AsAction; class SendTeamReportEmail { use AsAction; public function handle(Team $team, bool $fullReport false): void { // 可复用业务逻辑准备报告并发送给 $team-users。 } public function asJob(Team $team): void { // 队列专属分支缺省时 JobDecorator 会回退到 handle(...)。 $this-handle($team, true); } }二、dispatch 家族六种触发方式AsJobtrait 为 Action 提供了一组静态 dispatch 辅助方法job.md 对每一种都给出了示例与语义这里完整继承2.1dispatch—— 异步入队SendTeamReportEmail::dispatch($team);Coolify 中的实际案例容器状态巡检 Action GetContainersStatus 会检查「容器反复重启超过限制」的应用并触发停机它通过StopApplication::dispatch(...)异步派发停机 Action见 app/Actions/Docker/GetContainersStatus.php避免在状态轮询的同步请求路径中执行耗时的 Docker 操作。2.2dispatchIf/dispatchUnless—— 条件派发SendTeamReportEmail::dispatchIf($team-plan premium, $team); SendTeamReportEmail::dispatchUnless($team-plan free, $team);前者仅在条件为真时异步派发后者在条件为假时派发。适合「满足某订阅/开关条件才入队」的场景避免把条件判断散落到调用方。2.3dispatchSync/dispatchNow—— 同步执行SendTeamReportEmail::dispatchSync($team); SendTeamReportEmail::dispatchNow($team); // dispatchSync 的别名Coolify 中大量使用dispatchSync让「需要立即拿到结果」的操作绕过 worker。真实调用点包括app/Jobs/ServerCheckJob.phpConnectProxyToNetworksJob::dispatchSync($this-server)app/Jobs/UpdateCoolifyJob.phpCheckForUpdatesJob::dispatchSync()app/Livewire/Server/Destinations.php 与 app/Models/StandaloneDocker.php新增/变更网络后立即同步连接 proxy 网络。ServerCheckJob中的注释app/Jobs/PushServerUpdateJob.php 附近亦有类似说明指出按需触发的场景新网络、服务部署使用dispatchSync()直接执行并绕过常规调度这正是同步派发在 Coolify 中的典型定位。2.4dispatchAfterResponse—— 响应发出后同步执行SendTeamReportEmail::dispatchAfterResponse($team);在 HTTP 响应发出之后同步执行 Action适合「不阻塞响应、但希望不依赖 worker 也能完成」的收尾任务如清理、日志归档。三、Job 包装与链式编排makeJob、makeUniqueJob、withChain3.1makeJob与makeUniqueJob这两个方法把 Action 包装成队列 Job 实例便于使用 Laravel 原生的dispatch()辅助函数或参与链式执行dispatch(SendTeamReportEmail::makeJob($team)); dispatch(SendTeamReportEmail::makeUniqueJob($team));makeUniqueJob创建UniqueJobDecorator包装器——通常当 Action 实现ShouldBeUnique时会自动走唯一 Job 逻辑也可以在需要时强制使用。3.2withChain与Bus::chainwithChain把一组 Job 挂到当前 Action 成功处理之后依次执行$chain [ OptimizeTeamReport::makeJob($team), SendTeamReportEmail::makeJob($team), ]; CreateNewTeamReport::withChain($chain)-dispatch($team);等价的Bus::chain(...)写法use Illuminate\Support\Facades\Bus; Bus::chain([ CreateNewTeamReport::makeJob($team), OptimizeTeamReport::makeJob($team), SendTeamReportEmail::makeJob($team), ])-dispatch();Coolify 的跨服务器资源迁移 MigrateResourceToDestination 正是这一模式的真实应用它把若干卷迁移 Job 与一个收尾 Job 串成链条——if ($jobs ! []) { Bus::chain([ ...$jobs, // VolumeCloneJob / HostPathCloneJob new FinalizeResourceMigrationJob($resource, $destination), ])-dispatch(); }即先逐个执行VolumeCloneJob/HostPathCloneJob迁移卷数据全部成功后才由FinalizeResourceMigrationJob更新目标服务器信息任何一步失败链条即中断不会误改资源归属。3.3 链条断言use Illuminate\Support\Facades\Bus; Bus::fake(); Bus::assertChained([ CreateNewTeamReport::makeJob($team), OptimizeTeamReport::makeJob($team), SendTeamReportEmail::makeJob($team), ]);用于在测试中验证「哪些 Job、以什么顺序」被编排进了链条。四、队列测试断言assertPushed*家族job.md 定义了三个基于Queue::fake()的断言方法测试时不需要真实的队列驱动4.1assertPushed—— 断言已入队use Illuminate\Support\Facades\Queue; Queue::fake(); SendTeamReportEmail::assertPushed(); SendTeamReportEmail::assertPushed(3); SendTeamReportEmail::assertPushed($callback); SendTeamReportEmail::assertPushed(3, $callback);$callback会收到四个参数Action 实例、派发时传入的参数、JobDecorator实例、队列名。4.2assertNotPushed—— 断言未入队Queue::fake(); SendTeamReportEmail::assertNotPushed(); SendTeamReportEmail::assertNotPushed($callback);4.3assertPushedOn—— 断言入队到指定队列Queue::fake(); SendTeamReportEmail::assertPushedOn(reports); SendTeamReportEmail::assertPushedOn(reports, 3); SendTeamReportEmail::assertPushedOn(reports, $callback); SendTeamReportEmail::assertPushedOn(reports, 3, $callback);在 Coolify 的测试套件中可以看到同类断言的落地形态例如 tests/Feature/Api/LifecycleApisTest.php 中Queue::fake(); // ... 触发删除/定时任务 API Queue::assertPushed(DeleteResourceJob::class); Queue::assertPushed(ScheduledTaskJob::class, fn (ScheduledTaskJob $job) $job-task-is($task));tests/Feature/Api/DeploymentCancellationApiTest.php 则用回调断言排除了被取消的部署、保留了下一个部署的队列条目。这说明项目测试遵循 job.md 的 checklist 要求队列行为必须有Queue::fake() 断言覆盖。五、JobDecorator钩子与属性全解Action 被作为 Job 派发时框架会用JobDecorator包装它job.md 列出的钩子/属性是配置重试、唯一性、超时与失败处理的完整工具箱。5.1asJob—— 队列执行入口在被作为 Job 派发时调用如果 Action 没有定义该方法则回退到handle(...)public function asJob(Team $team): void { $this-handle($team, true); }skill 文档给出的项目级增强形态见 .agents/skills/laravel-actions/SKILL.md是asJob(JobDecorator $job, Demo $demo)——第一个参数直接拿到JobDecorator可读取尝试次数等元数据并做队列专属分支最后仍委托给handle(...)。5.2getJobMiddleware—— 队列中间件public function getJobMiddleware(array $parameters): array { return [new RateLimited(reports)]; }5.3configureJob—— 编程式配置 JobDecoratoruse Lorisleiva\Actions\Decorators\JobDecorator; public function configureJob(JobDecorator $job): void { $job-onConnection(my_connection) -onQueue(my_queue) -through([my_middleware]) -chain([my_chain]) -delay(60); }这是属性方式的「函数版」适合按参数动态决定连接、队列、延迟。5.4 队列与连接属性成员含义示例$jobConnection队列连接public string $jobConnection my_connection;$jobQueue队列名public string $jobQueue my_queue;Coolify 中的真实用法GetContainersStatus 与 CheckUpdates 都声明了public string $jobQueue high;把容器状态巡检、系统包更新检查这类高频轮询任务放入high队列与 Horizon 的 supervisor 分组配合实现优先级隔离仓库中 Horizon 相关配置参考 .agents/skills/configuring-horizon/references/supervisors.md。5.5 重试策略属性与方法成员含义示例$jobTries最大尝试次数public int $jobTries 10;$jobMaxExceptions判定失败前允许的最大未处理异常数public int $jobMaxExceptions 3;$jobBackoff重试延迟秒public int $jobBackoff 60;getJobBackoff()重试延迟支持 int 或按次递增数组return [30, 60, 120];$jobTimeout执行超时秒public int $jobTimeout 60 * 30;$jobRetryUntil重试截止时间戳public int $jobRetryUntil 1610191764;getJobRetryUntil()以DateTime表达重试截止return now()-addMinutes(30);skill 文档给出的完整项目形态示例Job Action 重试控制组合class GetDemoData { use AsAction; public int $jobTries 3; public int $jobMaxExceptions 3; public function getJobRetryUntil(): DateTime { return now()-addMinutes(30); } public function getJobBackoff(): array { return [60, 120]; } public function getJobUniqueId(Demo $demo): string { return $demo-id; } public function handle(Demo $demo): void { // Core business logic. } public function asJob(JobDecorator $job, Demo $demo): void { // Queue-specific orchestration and retry behavior. $this-handle($demo); } }5.6 唯一性控制Unique Job成员含义getJobUniqueId(...)/$jobUniqueId唯一键参数化或静态getJobUniqueFor(...)/$jobUniqueFor唯一锁持有秒数getJobUniqueVia()唯一锁使用的 cache 驱动public function getJobUniqueId(Team $team): int { return $team-id; } public string $jobUniqueId some_static_key; // 静态键替代 public function getJobUniqueFor(Team $team): int { return $team-role premium ? 1800 : 3600; } public int $jobUniqueFor 3600; // 属性替代 public function getJobUniqueVia() { return Cache::driver(redis); }唯一性对 Coolify 这类 PaaS 尤其重要状态轮询、备份、清理类任务天然存在并发重复派发的风险用ShouldBeUnique 唯一键可以把同一资源的重复队列执行折叠掉。5.7 展示名、标签、缺失模型与失败回调public function getJobDisplayName(): string { return Send team report email; } public function getJobTags(Team $team): array { return [report, team:.$team-id]; } public bool $jobDeleteWhenMissingModels true; public function getJobDeleteWhenMissingModels(): bool { return true; } public function jobFailed(?Throwable $e, ...$parameters): void { // Notify users, report errors, trigger compensations... }getJobDisplayName/getJobTags让 Horizon 之类的监控面板能识别任务$jobDeleteWhenMissingModels控制「被序列化的模型已删除时是否直接丢弃该 Job」jobFailed接收异常与派发参数是补偿/告警的天然挂载点。六、Checklist 与常见陷阱job.md 的收尾清单Checklist原文要点同步/异步派发方法与用例匹配dispatchvsdispatchSyncvsdispatchAfterResponse队列配置在需要时显式声明$jobConnection、$jobQueue、configureJob重试/退避/超时策略是有意设计的而不是默认值asJob(...)除非确需队列专属分支否则委托给handle(...)队列测试使用Queue::fake()与 Action 断言assertPushed*。常见陷阱Common pitfalls把领域逻辑只写进asJob(...)导致 HTTP/命令等入口拿不到这段逻辑重型 Job 忘记配置唯一性/超时/重试测试中缺少队列专属断言队列行为没有覆盖。Coolify 源码印证了这套纪律高频状态类 Action 显式声明$jobQueue highapp/Actions/Server/CheckUpdates.php、app/Actions/Docker/GetContainersStatus.php长耗时编排使用Bus::chain收尾 Job 保证原子推进app/Actions/Shared/MigrateResourceToDestination.php同步语义在按需路径上显式使用dispatchSyncapp/Jobs/ServerCheckJob.php。七、延伸阅读仓库内路径主题路径Job 入口点参考本文主体.agents/skills/laravel-actions/references/job.mdAction 总工作流与项目约定.agents/skills/laravel-actions/SKILL.md其他入口点参考object.md、controller.md、listener.md、command.md、with-attributes.md测试 Fake 深度参考testing-fakes.md、troubleshooting.md真实 Job Action 示例app/Actions/Docker/GetContainersStatus.php、app/Actions/Server/CheckUpdates.php、app/Actions/Shared/MigrateResourceToDestination.php队列断言测试示例tests/Feature/Api/LifecycleApisTest.php、tests/Feature/Api/DeploymentCancellationApiTest.php包版本声明composer.json【免费下载链接】coolifyAn open-source, self-hostable PaaS alternative to Vercel, Heroku Netlify that lets you easily deploy static sites, databases, full-stack applications and 280 one-click services on your own servers.项目地址: https://gitcode.com/GitHub_Trending/co/coolify创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表