PHP速学视频免费教程(入门到精通)
PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
在现代微服务或独立服务架构中,为了实现更便捷的伸缩性、更安全的发布流程以及更清晰的职责划分,将 Web 应用与后端批处理/作业服务部署在不同的代码仓库和 Laravel 应用程序中是一种常见的实践。例如,Web 应用负责用户交互和API请求,而批处理应用则专门处理耗时任务、数据同步或后台计算。
然而,这种架构也带来了一个挑战:Web 应用如何将任务安全可靠地传递给批处理应用进行处理?传统的 Laravel 队列机制通常假定队列工作者(queue worker)与任务分发者(dispatcher)在同一个 Laravel 应用实例中。如果 Web 应用分发的任务需要由运行在批处理服务器上的另一个 Laravel 应用实例来处理,直接使用常规的 Job::dispatch() 似乎会遇到障碍,因为队列工作者将无法找到或执行Web应用特有的 Job 类。
一些开发者可能会考虑使用 Redis 的 Pub/Sub 机制作为中间层,Web 应用发布消息,批处理应用订阅并触发其内部的 Laravel 队列。但这种方案存在弊端,例如在部署更新时重启 supervisor 守护进程,可能导致未处理的消息丢失。虽然可以通过 pm2 等工具实现滚动重启来缓解,但其复杂性仍高于直接的队列方案。
令人惊喜的是,Laravel 的队列机制本身就能够优雅地解决这个问题,而无需引入额外的 Pub/Sub 层。核心思想在于:在 Web 应用和批处理应用中定义完全相同的 Job 类签名。
当一个 Job 被分发时,Laravel 实际上是将 Job 类的完全限定名(Fully Qualified Class Name, FQCN)以及其构造函数中传递的参数进行序列化,然后存储到队列驱动(例如 Redis)中。当队列工作者从队列中取出任务时,它会根据存储的 FQCN 在自己的应用程序环境中查找并实例化该 Job 类,然后执行其 handle() 方法。
这意味着,只要 Web 应用和批处理应用中 App\Jobs\SomeJob 的命名空间、类名、属性以及构造函数签名保持一致,批处理应用就能够成功地反序列化并执行由 Web 应用分发的任务。
在 Web 应用(例如 app 1)中,我们定义一个 Job 类。在这个应用中,handle() 方法可以是一个空实现,或者包含一些仅用于 Web 应用的逻辑(如果需要)。关键是它的构造函数和属性需要与批处理应用中的 Job 定义保持一致。
// web repo - app 1: App\Jobs\SomeJob.php <?php namespace App\Jobs; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; class SomeJob implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; private int $userId; private string $someParam; /** * 创建一个新的任务实例。 * * @param int $userId 用户ID * @param string $someParam 某些参数 */ public function __construct(int $userId, string $someParam) { $this->userId = $userId; $this->someParam = $someParam; } /** * 在Web应用中,此方法可以为空或仅包含占位逻辑。 * 实际的业务逻辑将在批处理应用中执行。 * * @return void */ public function handle() { // 实际实现请参考批处理应用中的同名文件 } }
在 Web 应用的任何控制器、服务或事件监听器中,我们可以像往常一样分发这个 Job:
// 在Web应用中分发任务 use App\Jobs\SomeJob; // 假设我们有一些用户ID和参数 $userId = 123; $someParam = 'example_data'; SomeJob::dispatch($userId, $someParam);
当 SomeJob::dispatch() 被调用时,Laravel 会将 App\Jobs\SomeJob 这个字符串以及 $userId 和 $someParam 的值序列化后,存入配置的队列驱动(如 Redis)中。
在批处理应用(例如 app 2)中,我们也需要定义一个完全相同的 App\Jobs\SomeJob 类。不同之处在于,这里的 handle() 方法将包含实际的业务逻辑。
// batch repo - app 2: App\Jobs\SomeJob.php <?php namespace App\Jobs; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; class SomeJob implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; private int $userId; private string $someParam; /** * 创建一个新的任务实例。 * * @param int $userId 用户ID * @param string $someParam 某些参数 */ public function __construct(int $userId, string $someParam) { $this->userId = $userId; $this->someParam = $someParam; } /** * 执行任务。 * 这是任务的实际业务逻辑所在。 * * @return void */ public function handle() { // 实际的业务实现 echo "Processing Job for User ID: " . $this->userId . ", Param: " . $this->someParam . PHP_EOL; // 例如,可以进行数据库操作、API调用、文件处理等 } }
为了让批处理应用能够处理这些任务,我们需要在其服务器上运行 Laravel 队列工作者:
# 在批处理服务器上运行队列工作者 php artisan queue:work --sleep=3 --tries=1 --delay=1
当 queue:work 命令运行时,它会从配置的队列驱动中拉取任务。一旦拉取到由 Web 应用分发的 App\Jobs\SomeJob 任务,批处理应用的 Laravel 实例就会根据其自身的 App\Jobs\SomeJob 定义来实例化并执行 handle() 方法。
这种方案之所以可行,是因为 Laravel 队列在序列化和反序列化 Job 时,主要依赖以下信息:
当 Web 应用分发 Job 时,它将这些信息打包并存储到队列中。当批处理应用的队列工作者从队列中读取任务时,它会:
因此,handle() 方法的实际执行逻辑完全取决于运行队列工作者的那个 Laravel 应用实例中 Job 类的定义。这甚至允许 Web 应用和批处理应用使用不同版本的 Laravel(例如一个 Laravel 8,一个 Laravel 5.7),只要 Job 类的基本签名和序列化兼容性没有发生根本性改变。
尽管这种方法简单而有效,但在实际应用中仍需注意以下几点:
通过在独立的 Laravel 应用之间共享 Job 类的定义,我们可以巧妙地利用 Laravel 队列的内在机制,实现跨应用的异步任务分发与处理。这种方法避免了复杂的 Pub/Sub 模式,简化了部署和维护,同时提供了高度的解耦和扩展性。理解其背后的序列化原理,并遵循上述最佳实践,将有助于构建更健壮、可维护的分布式 Laravel 应用。
已抢7569个
抢已抢97331个
抢已抢15252个
抢已抢53946个
抢已抢198259个
抢已抢88323个
抢