Laravel 队列与事件开发全解析
在 Laravel 开发中,队列和事件是两个非常重要的功能,它们可以帮助我们优化应用程序的性能和响应速度。下面将详细介绍 Laravel 中队列和事件的使用方法。
队列的使用
队列在 Laravel 中主要用于处理一些耗时的任务,将这些任务放入队列中,由队列工作者(Queue Worker)异步处理,从而提高应用程序的响应速度。
1. 自定义队列
在队列服务器中,可以指定将作业推送到哪个命名队列。例如,可以根据队列的重要性进行区分,命名为
low
和
high
。可以使用
onQueue()
方法自定义要将作业推送到的队列:
dispatch((new DoThingJob)->onQueue('high'));
2. 自定义延迟
可以使用
delay()
方法自定义队列工作者在处理作业之前应等待的时间,该方法接受一个整数,表示延迟作业的秒数:
// 延迟一分钟后将作业释放给队列工作者
dispatch((new DoThingJob)->delay(60));
需要注意的是,Amazon SQS 不允许延迟超过 15 分钟。
3. 运行队列工作者
队列工作者是一个 Artisan 命令,它会一直运行(直到手动停止),负责从队列中拉取作业并运行它们:
php artisan queue:work
这个命令会启动一个守护进程,“监听”队列。每当队列中有作业时,它会拉取第一个作业,处理它,删除它,然后继续处理下一个。如果队列中没有作业,它会“休眠”一段可配置的时间,然后再次检查是否有更多作业。
可以通过以下参数来定义队列工作者的行为:
-
--timeout
:作业在队列监听器停止之前可以运行的秒数。
-
--sleep
:当队列中没有作业时,监听器应“休眠”的秒数。
-
--tries
:每个作业在被删除之前允许尝试的次数。
- 第一个参数:队列工作者应监听的连接。
-
--queue=
:队列工作者应监听的队列。
例如:
php artisan queue:work redis --timeout=60 --sleep=15 --tries=3 --queue=high,medium
也可以使用
php artisan queue:work
只处理一个作业。
4. 错误处理
在处理作业时,如果发生错误,队列监听器会将该作业重新释放回队列,直到作业成功完成或达到最大尝试次数。
-
限制尝试次数
:最大尝试次数由传递给
queue:listen或queue:workArtisan 命令的--tries开关定义。如果不设置--tries或设置为 0,队列监听器将允许无限重试,这可能会导致应用程序因不断重试无法完成的作业而变慢。建议将最大重试次数的默认起始值设置为 3:
php artisan queue:listen --tries=3
可以使用
attempts()
方法检查作业已经尝试的次数:
public function handle()
{
...
if ($this->attempts() > 3) {
//
}
}
- 处理失败的作业 :一旦作业超过了允许的重试次数,就被视为“失败”的作业。在处理失败的作业之前,需要创建“失败作业”数据库表:
php artisan queue:failed-table
php artisan migrate
可以在作业类中定义
failed()
方法,当作业失败时运行该方法:
class CrunchReports implements ShouldQueue
{
...
public function failed()
{
// Do whatever you want
}
}
还可以注册一个全局的失败作业处理程序,在
AppServiceProvider
的
boot()
方法中添加以下代码:
use Illuminate\Support\Facades\Queue;
...
public function boot()
{
Queue::failing(function ($connection, $job, $data) {
// Do whatever you want
});
}
此外,还有一套 Artisan 工具用于与失败作业表进行交互:
-
php artisan queue:failed
:显示失败作业的列表。
-
php artisan queue:retry <id>
:重试指定 ID 的失败作业。
-
php artisan queue:retry all
:重试所有失败作业。
-
php artisan queue:forget <id>
:删除指定 ID 的失败作业。
-
php artisan queue:flush
:删除所有失败作业。
5. 控制队列
在处理作业时,可以添加条件来决定是否将作业释放回队列以便稍后重新启动,或者永久删除作业。
-
释放作业回队列
:使用
release()
命令:
public function handle()
{
...
if (condition) {
$this->release($numberOfSecondsToDelayBeforeRetrying);
}
}
- 删除作业 :在处理作业时,只需返回即可删除作业:
public function handle()
{
...
if ($jobShouldBeDeleted) {
return;
}
}
6. 队列支持的其他功能
队列的主要用途是推送作业,但也可以使用
Mail::queue
功能对邮件进行排队,还可以对 Artisan 命令进行排队。
事件的使用
事件是一种通知机制,用于告知应用程序发生了某些事情。与作业不同,作业是告诉应用程序应该做什么,而事件是告诉应用程序某件事情已经发生。
1. 事件的概念
事件是一种通知,表明某件事情已经发生,例如
UserSubscribed
、
UserSignedUp
或
ContactWasAdded
。有些事件可能由框架本身触发,例如 Eloquent 模型在保存、创建或删除时会触发事件,也有些事件是由应用程序代码手动触发的。
2. 触发事件
有三种方式可以触发事件:
- 使用
Event
门面:
Event::fire(new UserSubscribed($user, $plan));
-
注入
Dispatcher:
$dispatcher = app(Illuminate\Contracts\Events\Dispatcher);
$dispatcher->fire(new UserSubscribed($user, $plan));
-
使用
event()全局辅助函数:
event(new UserSubscribed($user, $plan));
建议使用全局辅助函数。
可以使用
make:event
Artisan 命令创建一个事件:
php artisan make:event UserSubscribed
生成的事件类默认模板如下:
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class UserSubscribed
{
use InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Get the channels the event should be broadcast on.
*
* @return Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}
可以向事件中注入数据:
class UserSubscribed
{
use InteractsWithSockets, SerializesModels;
public $user;
public $plan;
public function __construct($user, $plan)
{
$this->user = $user;
$this->plan = $plan;
}
}
3. 监听事件
要监听事件,首先需要创建一个事件监听器。例如,每当有新用户订阅时,向应用程序的所有者发送电子邮件:
php artisan make:listener EmailOwnerAboutSubscription --event=UserSubscribed
生成的事件监听器默认模板如下:
<?php
namespace App\Listeners;
use App\Events\UserSubscribed;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class EmailOwnerAboutSubscription
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param UserSubscribed $event
* @return void
*/
public function handle(UserSubscribed $event)
{
//
}
}
可以修改
handle()
方法来实现具体的逻辑,例如发送电子邮件:
use Illuminate\Contracts\Mail\Mailer;
class EmailOwnerAboutSubscription
{
protected $mailer;
public function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
}
public function handle(UserSubscribed $event)
{
$this->mailer->send(
new OwnerSubscriptionEmail($event->user, $event->plan)
);
}
}
最后,需要在
EventServiceProvider
类的
$listen
属性中设置监听器来监听事件:
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
\App\Events\UserSubscribed::class => [
\App\Listeners\EmailOwnerAboutSubscription::class,
],
];
4. 事件订阅者
Laravel 还提供了事件订阅者的概念,它是一个包含多个方法的类,这些方法作为独立的监听器来处理不同的事件,并且包含了哪个方法应该处理哪个事件的映射。
示例事件订阅者如下:
<?php
namespace App\Listeners;
class UserEventSubscriber
{
public function onUserSubscription($event)
{
// Handles the UserSubscribed event
}
public function onUserCancellation($event)
{
// Handles the UserCancelled event
}
public function subscribe($events)
{
$events->listen(
\App\Events\UserSubscribed::class,
'App\Listeners\UserEventSubscriber@onUserSubscription'
);
$events->listen(
\App\Events\UserCancelled::class,
'App\Listeners\UserEventSubscriber@onUserCancellation'
);
}
}
需要在
App\Providers\EventServiceProvider
中添加订阅者的类名到
$subscribe
属性:
class EventServiceProvider extends ServiceProvider
{
...
protected $subscribe = [
\App\Listeners\UserEventSubscriber::class
];
}
事件通过 WebSockets 广播和 Laravel Echo
WebSocket 是一种由 Pusher 推广的协议,它可以在 Web 设备之间提供近乎实时的通信。Laravel 内置了一套工具,使得将一个或多个事件广播到 WebSocket 服务器变得容易。
1. WebSocket 协议
WebSocket 协议通过直接在客户端和服务器之间打开连接,避免了依赖 HTTP 请求进行信息传递,像 Gmail 和 Facebook 的聊天框等工具就是基于 WebSockets 实现的。
2. Laravel Echo
Laravel Echo 是一个更强大的工具,用于更复杂的事件广播。如果需要存在通知,或者希望保持丰富的前端数据模型与 Laravel 应用同步,可以使用 Laravel Echo。
3. 配置和设置
可以在
config/broadcasting.php
中找到事件广播的配置设置。Laravel 支持三种广播驱动:Pusher(付费 SaaS 服务)、Redis(本地运行的 WebSocket 服务器)和 log(用于本地开发和调试)。
为了使事件广播快速进行,Laravel 将广播指令推送到队列中,因此需要运行一个队列工作者(或在本地开发时使用同步队列驱动)。Laravel 建议队列工作者在查找新作业之前默认延迟 3 秒,但在事件广播中,可能会发现某些事件需要一两秒才能广播。为了加快速度,可以将队列设置更新为只等待 1 秒再查找新作业。
4. 广播事件
要广播事件,需要让事件类实现
Illuminate\Contracts\Broadcasting\ShouldBroadcast
接口,并添加
broadcastOn()
方法,该方法将返回一个字符串数组或
Channel
对象数组,每个对象代表一个 WebSocket 通道。
示例:
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class UserSubscribed extends Event implements ShouldBroadcast
{
use InteractsWithSockets, SerializesModels;
public $user;
public $plan;
public function __construct($user, $plan)
{
$this->user = $user;
$this->plan = $plan;
}
public function broadcastOn()
{
// String syntax
return [
'users.' . $this->user->id,
'admins'
];
// Channel object syntax
return [
new Channel('users.' . $this->user->id),
new Channel('admins'),
// If it were a private channel: new PrivateChannel('admins'),
// If it were a presence channel: new PresenceChannel('admins'),
];
}
}
默认情况下,事件的任何公共属性将被序列化为 JSON 并作为广播事件的数据发送。可以通过
broadcastWith()
方法返回一个数据数组来覆盖默认行为:
public function broadcastWith()
{
return [
'userId' => $this->user->id,
'plan' => $this->plan
];
}
还可以使用
onQueue()
方法自定义事件被推送到的队列:
public function onQueue()
{
return 'websockets-for-faster-processing'
}
5. 接收消息
如果选择自己托管 Redis WebSockets 服务器,Laravel 文档中有关于如何使用
socket.io
和
ioredis
设置它的详细说明。但更常见的是使用 Pusher,它有一个慷慨的免费计划,并且其 JavaScript SDK 几乎不需要你做任何工作就能处理所有的身份验证和通道管理。
可以使用以下代码来订阅事件:
<script src="https://js.pusher.com/3.1/pusher.min.js"></script>
<script>
// Globally, perhaps; just a sample of how to get data in
var App = {
'userId': 5,
'pusherKey': 'your-pusher-api-key-here'
};
// Locally
var pusher = new Pusher(App.pusherKey);
var pusherChannel = pusher.subscribe('users.' + App.userId);
pusherChannel.bind('App\\Events\\UserSubscribed', (data) => {
console.log(data.user, data.plan);
});
</script>
在 JavaScript 中,由于
\
是控制字符,需要使用
\\
来表示反斜杠。
要从 Laravel 向 Pusher 发布事件,需要从 Pusher 账户仪表板获取 Pusher 密钥、秘密和应用 ID,并将它们设置在
.env
文件中。
通过以上介绍,我们详细了解了 Laravel 中队列和事件的使用方法,以及如何通过 WebSockets 进行事件广播。合理使用这些功能可以大大提高应用程序的性能和响应速度。
Laravel 队列与事件开发全解析(续)
Laravel Echo 的进一步应用
前面提到 Laravel Echo 是用于更复杂事件广播的强大工具,下面详细探讨其使用。
1. Laravel Echo 的优势
Laravel Echo 能提供存在通知功能,比如可以实时知道哪些用户在线。同时,它有助于保持前端数据模型与 Laravel 应用同步,让前端开发者更方便地处理后端事件。
2. 引入 Laravel Echo
要使用 Laravel Echo,首先需要引入其 JavaScript 库。可以通过 npm 进行安装:
npm install --save laravel-echo pusher-js
安装完成后,在前端代码中进行配置。在
resources/js/bootstrap.js
文件中添加以下代码:
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
window.Pusher = Pusher;
window.Echo = new Echo({
broadcaster: 'pusher',
key: process.env.MIX_PUSHER_APP_KEY,
cluster: process.env.MIX_PUSHER_APP_CLUSTER,
encrypted: true
});
这里使用 Pusher 作为广播器,需要在
.env
文件中配置 Pusher 的相关信息。
3. 使用 Laravel Echo 监听事件
配置好 Laravel Echo 后,就可以使用它来监听事件。例如,监听
UserSubscribed
事件:
window.Echo.channel('users.' + userId)
.listen('UserSubscribed', (e) => {
console.log(e.user, e.plan);
});
这里通过
channel
方法指定要监听的通道,
listen
方法指定要监听的事件。
队列与事件的流程图分析
下面通过 mermaid 流程图来展示队列和事件的主要流程。
1. 队列处理流程
graph TD;
A[作业创建] --> B[作业入队];
B --> C{队列是否有作业};
C -- 有作业 --> D[队列工作者拉取作业];
D --> E[处理作业];
E -- 成功 --> F[删除作业];
E -- 失败 --> G[重新入队];
G --> C;
C -- 无作业 --> H[队列工作者休眠];
H --> C;
这个流程图展示了队列作业的处理过程。作业创建后入队,队列工作者不断检查队列是否有作业,有则拉取处理,成功则删除,失败则重新入队。无作业时队列工作者会休眠一段时间后再次检查。
2. 事件处理流程
graph TD;
A[事件触发] --> B{是否有监听器};
B -- 有监听器 --> C[监听器响应事件];
B -- 无监听器 --> D[事件无响应];
C --> E[执行监听器逻辑];
事件触发后,会检查是否有监听器。如果有监听器,监听器会响应事件并执行相应逻辑;如果没有监听器,事件则无响应。
队列和事件的性能优化
在实际应用中,为了提高队列和事件的性能,需要进行一些优化。
1. 队列性能优化
-
合理设置队列参数
:根据业务需求,合理设置
--timeout、--sleep和--tries等参数。例如,对于一些对实时性要求较高的作业,可以将--timeout设置得小一些,避免作业长时间占用资源。 -
使用多个队列
:根据作业的重要性和类型,使用不同的队列。例如,将重要的作业放在
high队列,次要的作业放在low队列,让队列工作者分别处理不同队列的作业,提高处理效率。
2. 事件性能优化
- 减少不必要的监听器 :只添加必要的监听器,避免过多的监听器导致性能下降。
-
优化监听器逻辑
:在监听器的
handle方法中,尽量减少复杂的计算和数据库操作,提高响应速度。
队列和事件的实际应用场景
队列和事件在很多实际场景中都有广泛应用,下面介绍一些常见的场景。
1. 队列的应用场景
- 邮件发送 :将邮件发送任务放入队列中,由队列工作者异步处理,避免在用户请求时直接发送邮件导致响应时间过长。例如:
dispatch((new SendEmailJob($user, $email))->onQueue('email'));
- 文件处理 :对于一些大文件的处理,如文件上传后的压缩、转换等操作,可以将这些任务放入队列中,避免阻塞主线程。
2. 事件的应用场景
-
用户注册通知
:当用户注册成功后,触发
UserRegistered事件,通过监听器发送欢迎邮件、通知管理员等。
event(new UserRegistered($user));
-
订单状态更新
:当订单状态发生变化时,触发
OrderStatusChanged事件,更新相关的缓存、通知用户等。
总结
通过本文的介绍,我们全面了解了 Laravel 中队列和事件的使用方法,包括队列的自定义、延迟、错误处理、控制等,事件的触发、监听、订阅者以及通过 WebSockets 进行广播等。同时,还探讨了 Laravel Echo 的应用、队列和事件的流程图分析、性能优化以及实际应用场景。
合理运用队列和事件功能,可以将耗时任务异步处理,提高应用程序的性能和响应速度,实现更高效的开发。在实际项目中,开发者应根据具体需求,灵活配置和使用这些功能,以达到最佳的开发效果。
超级会员免费看
14

被折叠的 条评论
为什么被折叠?



