Laravelフレームワークの組み込みブロードキャスト機能がクライアントとのリアルタイム通信を実現する方法

不言
リリース: 2018-07-31 10:36:27
オリジナル
4649 人が閲覧しました

Laravelフレームワークは多くの開発パッケージの機能を統合していますが、確かに優れたフレームワークです。次の記事では、 Laravel 5.6 バージョンに基づいて、組み込みのブロードキャスト関数を使用してクライアントとのリアルタイム通信を実現する方法を説明します。

1.準備する

ブロードキャストシステム

ユーザー認証

イベントシステム

キューシステム

フロントエンドガイド

tlaverdure/laravel-echo-server

はい、これが必要な知識の予備です。

PHP自体はWebSocketをサポートしていないため、「サーバー」データを「クライアント」に送信できる間接層が必要です。つまり、リアルタイム通信の実現は、

「Laravel」→「間接層」

「間接層」→「クライアント」

の2つのステップに分かれます。間接層にどのような実装を使用するかについては、後で説明します。

2. 設定

上記のブロードキャスト システムのドキュメントによると、最初に次の設定作業を行う必要があります。

(1) まず、config/broadcasting.php または .env ファイルを変更します。この機能をオンにしてデバッグを容易にするために、ブロードキャスト デフォルト ドライバーがログに記録されていることを確認してください。

(2) Broadcastを使用するには、相互依存しているLaravelのイベントシステムを理解する必要があります。次に「ブロードキャスト」できるイベントを作成します。

app/Providers/EventServiceProvider.php を変更し、メンバー変数 $listen を追加します。

'App\Events\OrderShipped' => [
    'App\Listeners\SendShipmentNotification',
],
ログイン後にコピー

ここでは、このイベントに OrderShipped (注文は決済されました) という名前を付けます。

イベントの実行 (つまり、クライアントへのブロードキャスト) を非同期に変更する必要がある場合は、上記のキュー システムのドキュメントを参照してください。

(3) イベントを「ブロードキャスト」できるようにするには、イベント クラスに ShouldBroadcast インターフェイスを継承させる必要があります。

app/Events/OrderShipped.php を開き、クラス定義を次のように変更します:

class OrderShipped implements ShouldBroadcast
ログイン後にコピー

(4) ShouldBroadcast インターフェイスには、このイベントがどの「チャネル」であるかをフレームワークに通知するために使用される、broadcastOn メソッドの実装が必要です。に送信された。

Laravel のブロードキャスト システムでは、複数のチャネルが存在することができます。ユーザー名を使用して異なるチャネルを区別できるため、異なるユーザーが異なるチャネルを使用して異なるメッセージを取得し、異なるクライアントと個別に通信できます。

もちろん、チャンネルに任意の名前を付けることもできますが、一定のルールを設けた方が良いでしょう。

Artisan コマンドによって生成されたイベント コードを使用しただけなので、ファイルの下部に broadcastOn メソッドの定義がすでに表示されています。これを少し変更します:

public function broadcastOn()
{
    return new Channel('orderStatus');
}
ログイン後にコピー

ここではチャネルに orderStatus という名前を付けて return します。つまり、このイベントがブロードキャストされると、orderStatus という名前のチャネルにブロードキャストされます。

これは「パブリックチャンネル」です。誰でもこのチャンネルを監視し、ブロードキャストメッセージを受信できます。 Laravel は「プライベート チャネル」も提供します。これは、権限の検証後にのみ正常に監視できます。それについては後で話します。

(5) デフォルトでは、Laravelはデータなしで「イベント名」をブロードキャストの「メッセージ名」として使用します。イベント クラス内にメンバー変数を追加し、クライアントにデータを送信するようにコンストラクターを変更できます。

//可添加任意成员变量
public $id;
//事件构造函数
public function __construct($id)
{
    $this->id = $id;
}
//自定义广播的消息名
public function broadcastAs()
{
    return 'anyName';
}
ログイン後にコピー

(6) 以上で、放送の基本的な仕組みはほぼ確立しました。次に、「イベントをトリガー」できる (つまり、ブロードキャストを送信できる) インターフェースが必要です。

routes/api.php に次のコードを追加します:

Route::get('/ship', function (Request $request)
{
    $id = $request->input('id');
    event(new OrderShipped($id)); // 触发事件
    return Response::make('Order Shipped!');
});
ログイン後にコピー

(7) 以上です。 Postman を開き、http://***/api/ship?id=1000 と入力して送信します。

storage/logs/laravel.log を開くと、さらにいくつかの行が見つかります:

[2018-03-02 01:41:19] local.INFO: Broadcasting [App\Events\OrderShipped] on channels [orderStatus] with payload:
{
    "id": "1000",
    "socket": null
}
ログイン後にコピー

おめでとうございます。ブロードキャスト ログ ドライバーが正常に構成されました。

3. ブロードキャスト

前のセクションでは、ブロードキャスト ドライバーとしてログを使用しました。これは、ブロードキャスト メッセージがログに記録されることを意味します。これには、冒頭で述べた「間接層」を使用する必要があります。

(1) まず、ドライバーをログからプッシャーに変更します。

Redis のインストール手順を節約するために、HTTP プロトコルを使用して、互換性のある「ローカル プッシャー サーバー」(つまり、間接層) に直接プッシュします。

(2) Laravel には組み込みの Broadcast Pusher Driver がないため、Composer を使用して Pusher PHP SDK をインストールする必要があります:

composer require pusher/pusher-php-server
ログイン後にコピー

(3) AppProvidersBroadcastServiceProvider を登録します。

Laravel 5.6 の場合、config/app.php のプロバイダー配列内の対応するコメントをコメント解除するだけで済みます。

(4) 次に、サーバーとクライアント間の通信のための「間接層」をインストールして構成する必要があります。

公式ドキュメントの推奨事項である tlaverdure/laravel-echo-server を使用します。 Node.js + Socket.IOを使用して実装されたWebSocketサーバーです。

Pusher HTTP API と互換性があるため、上記のように Redis の代わりに Driver を直接 Pusher に変更できます。

npm install -g laravel-echo-server
ログイン後にコピー

設定ファイルを初期化し、プロンプトに従って入力します。

laravel-echo-server init
ログイン後にコピー

laravel-echo-server.json を開いて、いくつかの主要な設定項目が正しいかどうかを確認してください:

"authHost": "http://xxx" // 确保能够访问到你的 Laravel 项目
"port": "6001" // 建议不作修改,这是与客户端通信的端口
"protocol": "http" // 与客户端通信的协议,支持 HTTPS
ログイン後にコピー

复制两个值:clients.appId 以及 clients.key,并修改 config/broadcasting.php。

'pusher' => [
    'driver' => 'pusher',
    'key' => env('PUSHER_APP_KEY'),
    'secret' => null,
    'app_id' => env('PUSHER_APP_ID'),
    'options' => [
        'host' => 'localhost',
        'port' => 6001,
    ],
],
ログイン後にコピー

顾名思义,将 appId 和 key 分别填入相应配置项或修改 .env 文件即可。

接下来,我们便可以使用命令 laravel-echo-server start 来启动服务器,监听来自 Laravel 的「广播」请求、以及来自客户端的「收听」请求,并转发相应广播消息。

(5)从这里开始我们将会配置前端部分。

首先,需要配置 CSRF Token。

如果你使用 Blade 模板引擎,则执行 php artisan make:auth 命令即可将 Token 加入 resources/views/layouts/app.blade.php。

若没有使用此模板文件,则可以直接在 Blade 模板文件首行直接写入 Meta 值。

为了便于测试,我们在 resources/views/welcome.blade.php 内添加:

<meta name="csrf-token" content="{{ csrf_token() }}">
ログイン後にコピー

对于前后端分离的项目,可关闭 CSRF Token 验证。

(6)其次,你需要引用 Socket.IO 的客户端 JS 文件,位置同上。

<script src="//{{ Request::getHost() }}:6001/socket.io/socket.io.js"></script>
ログイン後にコピー

(7)这里我们采用官方的 Laravel Echo 扩展包收听服务端广播。打开 resources/assets/js/bootstrap.js,修改最后几行即可。

import Echo from 'laravel-echo'

window.Echo = new Echo({
    broadcaster: &#39;socket.io&#39;,
    host: window.location.hostname + &#39;:6001&#39;
});
ログイン後にコピー

接着编写「收听频道」代码,非常简单:

Echo.channel(`orderStatus`) // 广播频道名称
    .listen(&#39;OrderShipped&#39;, (e) => { // 消息名称
        console.log(e); // 收到消息进行的操作,参数 e 为所携带的数据
    });
ログイン後にコピー

(8)安装依赖,编译前端文件。执行如下命令:

npm install
npm run dev
ログイン後にコピー

最后,在 Blade 模板内引用我们刚刚写好的 JS 脚本。由于 app.js 默认已经引用 bootstrap.js,所以我们只需要引用 app.js 即可。

我们在第(5)步的文件内添加:

<script src="{{ asset(&#39;/js/app.js&#39;) }}"></script>
ログイン後にコピー

(9)好了!在查看效果前,不要忘记执行第(4)步的最后一条命令,启动 Laravel Echo Server。

Laravel 默认已经定义首页路由渲染 welcome.blade.php 模板,现在只要使用浏览器访问应用 URL 即可。

如果查看 Chrome 控制台,没有任何错误产生;查看命令行窗口,没有错误输出;则说明客户端与服务器似乎已经正常建立 WebSocket 连接。

这时,你可以重新打开Postman,发送上一节中的请求。

再次查看如上两个窗口,会有惊喜哟。

4、私有频道

上一节我们成功实现可以被任何人收听的「共有频道」广播,那么如何与部分客户端进行通讯呢?仅仅依靠前端的验证是不够的。我们需要创建带有「认证」功能的「私有频道」。

(1)首先,打开 app/Providers/BroadcastServiceProvider.php,在上一节中我们已经注册此服务提供者,现在我们需要取消注释一部分代码。

public function boot()
{
    Broadcast::routes(); // 还记得 laravel-echo-server.json 的 authEndpoint 配置项吗?
    require base_path(&#39;routes/channels.php&#39;);
}
ログイン後にコピー

Broadcast::routes() 用于注册验证路由(即 /broadcasting/auth),当客户端收听频道时,Laravel Echo Server 会访问此路由,以验证客户端是否符合「认证」条件。

(2)Broadcast 认证分为两个部分:

使用 Laravel 内置的 Auth 系统进行认证。

根据自定义规则进行部分频道的认证。

(3)首先,需要配置 Auth 认证系统。

根据情况修改 .env 文件的数据库配置项后,只需要执行 php artisan make:auth 创建所需文件,再执行 php artisan migrate 创建所需数据库结构即可。

深入了解,请参考用户认证文档。

接下来我们在浏览器中打开你的应用,会发现右上角多了登录和注册,我们随意注册一个测试用户以备使用。

(4)接下来,配置频道认证。

还记得第(1)步的 routes/channels.php 吗,我们打开此文件。并新增一条频道认证规则。

注意:虽然此文件位于 routes 目录下,但并不是路由文件!在此定义后并不能访问,且无法使用分组、中间件。所有的验证路由都已经在 Broadcast::routes() 中定义。

Broadcast::channel(&#39;orderStatus&#39;, function ($user, $value) {
    return true; // or false
});
ログイン後にコピー

由于 Broadcast 已经使用 Auth 进行用户登录认证,所以我们只需无条件返回 true 即可实现:任何已登录用户都可以收听此频道。

(5)认证部分我们已经配置完成,接下来将共有频道的定义改为私有。

修改广播消息基于的事件类 app/Events/OrderShipped.php:

public function broadcastOn()
{
    return new PrivateChannel(&#39;orderStatus&#39;); // 私有频道
}
ログイン後にコピー

修改客户端收听代码 resources/assets/js/bootstrap.js。

Echo.private(`orderStatus`) // 私有频道
    .listen(&#39;OrderShipped&#39;, (e) => {
        console.log(e);
    });
ログイン後にコピー

(6)接下来,再次运行 Laravel Echo Server。使用浏览器打开你的应用首页,在未登录状态,可以看到 Echo Server 输出一个来自 Laravel 的 AccessDeniedHttpException 异常,提示用户认证失败,无法收听频道。

登录后,即可获得与上一节相同的预期结果。

(7)如上,我们成功实现所有登录用户均可收听私有频道。那么如何实现某部分用户可以收听,某部分用户不可以收听频道?例如:某个用户均有属于自己的频道,他只能收听自己的频道。请继续往下看。

首先,修改广播消息基于的事件类 app/Events/OrderShipped.php。你需要将频道命名修改为动态的值。

public $userId; // 新增成员变量 userId,不要忘记在构造函数内对其进行赋值

public function broadcastOn()
{
    return new PrivateChannel(&#39;orderStatus-&#39; . $this->userId); // 动态命名私有频道
}
ログイン後にコピー

其次,修改第(4)步中的 routes/channels.php 文件。Broadcast 支持使用通配符匹配「某一类」频道进行验证。

Broadcast::channel(&#39;orderStatus-{userId}&#39;, function ($user, $value) {
    // $user    Auth认证通过的用户模型实例
    // $value   频道规则匹配到的 userId 值
    return $user->id == $value; // 可使用任意条件验证此用户是否可监听此频道
});
ログイン後にコピー

最后别忘记修改前端收听的频道名称。

再次打开浏览器测试即可发现:在本例中,若客户端收听的频道不匹配当前用户 ID,则会报错。

(8)如上,我们成功实现对私有频道进行自定义规则的认证;但如果我们没有使用 Auth 认证系统,或采用了自己编写的用户认证中间件,该如何兼容呢?

经过一番源码调试,我发现 Broadcast 在 vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/ 文件夹下定义的 Broadcaster 内调用 $request->user() 进行了用户验证。

例如我们采用的 PusherBroadcaster.php:

/**
 * Authenticate the incoming request for a given channel.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return mixed
 * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
 */
public function auth($request)
{
    if (Str::startsWith($request->channel_name, [&#39;private-&#39;, &#39;presence-&#39;]) &&
        ! $request->user()) {
        throw new AccessDeniedHttpException;
    }
    $channelName = Str::startsWith($request->channel_name, &#39;private-&#39;)
                        ? Str::replaceFirst(&#39;private-&#39;, &#39;&#39;, $request->channel_name)
                        : Str::replaceFirst(&#39;presence-&#39;, &#39;&#39;, $request->channel_name);
    return parent::verifyUserCanAccessChannel(
        $request, $channelName
    );
}
ログイン後にコピー

由此可得,我们有两种方式实现。

第一种

直接注释 throw new AccessDeniedHttpException,并修改 app/Providers/BroadcastServiceProvider.php。

Broadcast::routes() 可接收一个参数。在 vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastManager.php 可查看其定义:

/**
 * Register the routes for handling broadcast authentication and sockets.
 *
 * @param  array|null  $attributes
 * @return void
 */
public function routes(array $attributes = null)
{
    if ($this->app->routesAreCached()) {
        return;
    }
    $attributes = $attributes ?: [&#39;middleware&#39; => [&#39;web&#39;]];
    $this->app[&#39;router&#39;]->group($attributes, function ($router) {
        $router->post(&#39;/broadcasting/auth&#39;, &#39;\\&#39;.BroadcastController::class.&#39;@authenticate&#39;);
    });
}
ログイン後にコピー

通过源码可知:此参数等效于 Route::group() 的第一个参数。所以我们只要将其修改为如下形式:

Broadcast::routes([&#39;middleware&#39; => [&#39;yourMiddleware&#39;])
ログイン後にコピー

并在中间件内进行用户认证;如未登录,照常抛出 AccessDeniedHttpException 异常即可。

第二种

在 vendor/laravel/framework/src/Illuminate/Http/Request.php 可以查看到 $request->user() 的定义。

/**
 * Get the user making the request.
 *
 * @param  string|null  $guard
 * @return mixed
 */
public function user($guard = null)
{
    return call_user_func($this->getUserResolver(), $guard);
}
ログイン後にコピー

如上可知,它使用 $this->userResolver 内的匿名函数获取用户模型。所以我们只需要在AuthServiceProvider 注册后,Broadcast 认证前,替换掉其 userResolver 即可。

例如:继承 Illuminate\Auth\AuthServiceProvider(vendor/laravel/framework/src/Illuminate/Auth/AuthServiceProvider.php),并重写 registerRequestRebindHandler 方法及构造函数,添加如下代码。

$request->setUserResolver(function ($guard = null) use ($app) {
    // 在此判断用户登录状态
    // 若登录,请返回 App\User 或其它用户模型实例
    // 未登录,请返回 null
});
ログイン後にコピー

修改 config/app.php,使用改造过的 AuthServiceProvider 类替换原服务提供者即可。

5、总结

至此,你已经建立对 Laravel Boardcast 的基本认识,成功入门「广播系统」。

另外,Laravel 5.6 新增一条关于 Broadcast 的新特性,避免在 routes/channels.php 文件内编写众多闭包导致的难以维护。详情可查看:Laravel 5.6 新版特性。

其实,本文只不过抛砖引玉而已。对于部署到生产环境,仍然存在许多问题,例如:

与常见 PHP 应用不同,广播基于 WebSocket 长连接;那么如何确保通信稳定性,如何剔除死链?

为了确保应用访问速度,广播通常是异步执行的;那么如何配置事件队列?如何将队列配置为异步?

如何确保 Laravel Echo Server 进程稳定运行?如何处理异常?是否需要关闭 Debug 模式?

如何确保 Laravel Echo 与服务端连接稳定?是否需要定时检查?如何捕捉并完善地处理所有异常?

客户端收听私有频道,若被服务器拒绝,是否需要断开连接?如何给予用户友好地提示?

……

当然,这些问题本文没有提到,但网络上已经有无数成熟、便捷的解决方案;更多高级用法也可以参照本文最初列出的官方文档。

以上就是本篇文章的全部内容了,更多内容请关注laravel入门教程!

相关文章推荐:

Laravel框架中管道设计模式之中间件的基本工作原理

Laravel框架实现发送短信验证功能代码,laravel发送短信

相关课程推荐:

全方位解读Laravel框架及实战视频教程

2017年最新のおすすめLaravelビデオチュートリアル5本

以上がLaravelフレームワークの組み込みブロードキャスト機能がクライアントとのリアルタイム通信を実現する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!