28、Laravel 队列与事件开发全解析

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:work Artisan 命令的 --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 的应用、队列和事件的流程图分析、性能优化以及实际应用场景。

合理运用队列和事件功能,可以将耗时任务异步处理,提高应用程序的性能和响应速度,实现更高效的开发。在实际项目中,开发者应根据具体需求,灵活配置和使用这些功能,以达到最佳的开发效果。

源码来自:https://pan.quark.cn/s/a3a3fbe70177 AppBrowser(Application属性查看器,不需要越狱! ! ! ) 不需要越狱,调用私有方法 --- 获取完整的已安装应用列表、打开和删除应用操作、应用运行时相关信息的查看。 支持iOS10.X 注意 目前AppBrowser不支持iOS11应用查看, 由于iOS11目前还处在Beta版, 系统API还没有稳定下来。 等到Private Header更新了iOS11版本,我也会进行更新。 功能 [x] 已安装的应用列表 [x] 应用的详情界面 (打开应用,删除应用,应用的相关信息展示) [x] 应用运行时信息展示(LSApplicationProxy) [ ] 定制喜欢的字段,展示在应用详情界面 介绍 所有已安装应用列表(应用icon+应用名) 为了提供思路,这里只用伪代码,具体的私有代码调用请查看: 获取应用实例: 获取应用名和应用的icon: 应用列表界面展示: 应用列表 应用运行时详情 打开应用: 卸载应用: 获取info.plist文件: 应用运行时详情界面展示: 应用运行时详情 右上角,从左往右第一个按钮用来打开应用;第二个按钮用来卸载这个应用 INFO按钮用来解析并显示出对应的LSApplicationProxy类 树形展示LSApplicationProxy类 通过算法,将LSApplicationProxy类,转换成了字典。 转换规则是:属性名为key,属性值为value,如果value是一个可解析的类(除了NSString,NSNumber...等等)或者是个数组或字典,则继续递归解析。 并且会找到superClass的属性并解析,superClass如...
基于遗传算法辅助异构改进的动态多群粒子群优化算法(GA-HIDMSPSO)的LSTM分类预测研究(Matlab代码实现)内容概要:本文研究了一种基于遗传算法辅助异构改进的动态多群粒子群优化算法(GA-HIDMSPSO),并将其应用于LSTM神经网络的分类预测中,通过Matlab代码实现。该方法结合遗传算法的局搜索能力改进的多群粒子群算法的局部优化特性,提升LSTM模型在分类任务中的性能表现,尤其适用于复杂非线性系统的预测问题。文中详细阐述了算法的设计思路、优化机制及在LSTM参数优化中的具体应用,并提供了可复现的Matlab代码,属于SCI级别研究成果的复现拓展。; 适合人群:具备一定机器学习和优化算法基础,熟悉Matlab编程,从事智能算法、时间序列预测或分类模型研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①提升LSTM在分类任务中的准确性收敛速度;②研究混合智能优化算法(如GAPSO结合)在神经网络超参数优化中的应用;③实现高精度分类预测模型,适用于电力系统故障诊断、电池健康状态识别等领域; 阅读建议:建议读者结合Matlab代码逐步调试运行,理解GA-HIDMSPSO算法的实现细节,重点关注种群划分、异构策略设计及LSTM的集成方式,同时可扩展至其他深度学习模型的参数优化任务中进行对比实验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值