Heim > Artikel > PHP-Framework > Wie die integrierte Broadcast-Funktion des Laravel-Frameworks eine Echtzeitkommunikation mit dem Client erreicht
Laravel Das Framework integriert die Funktionen vieler Entwicklungspakete. Obwohl es viele Dinge hat, ist es tatsächlich ein gutes Framework. Im nächsten Artikel erfahren Sie, wie Sie die integrierte Broadcast-Funktion verwenden, um eine Echtzeitkommunikation mit dem Client basierend auf der Version Laravel 5.6 zu erreichen.
1. Vorbereitung
Broadcast-System
Benutzerauthentifizierung
Ereignissystem
Warteschlangensystem
Front-End-Anleitung
tlaverdure/laravel-echo-server
Ja, das ist die Wissensreserve, die Sie brauchen.
Da PHP selbst WebSocket nicht unterstützt, benötigen wir eine Indirektionsschicht, die „Server“-Daten an „Client“ senden kann. Mit anderen Worten, die Realisierung der Echtzeitkommunikation kann grob in zwei Schritte unterteilt werden:
「Laravel」-> 「Indirekte Schicht」
「Indirekte Schicht」-> -> ;"Client"
Was die Implementierung der indirekten Schicht betrifft, werden wir später darüber sprechen.
2. Konfiguration
Gemäß der obigen Broadcast-Systemdokumentation müssen wir zunächst die folgenden Konfigurationsarbeiten durchführen.
(1) Ändern Sie zunächst die Datei config/broadcasting.php oder .env. Stellen Sie sicher, dass der Broadcast-Standardtreiber protokolliert ist, um diese Funktion zu aktivieren und uns das Debuggen zu erleichtern.
(2) Um Broadcast nutzen zu können, müssen Sie das Ereignissystem von Laravel verstehen, das voneinander abhängt. Als nächstes erstellen wir ein Ereignis, das „übertragen“ werden kann.
Ändern Sie app/Providers/EventServiceProvider.php und fügen Sie die Mitgliedsvariable $listen hinzu:
'App\Events\OrderShipped' => [ 'App\Listeners\SendShipmentNotification', ],
Hier nennen wir dieses Ereignis „OrderShipped“ (Bestellung wurde abgewickelt); führen Sie das php artisan event:generate Generate aus Ereignisklassen und ihre Zuhörer.
Wenn Sie die Ereignisausführung (d. h. die Übertragung an den Client) so ändern müssen, dass sie asynchron ist, lesen Sie bitte die obige Dokumentation zum Warteschlangensystem.
(3) Damit das Ereignis „gesendet“ werden kann, müssen wir der Ereignisklasse die Schnittstelle ShouldBroadcast erben lassen.
Öffnen Sie app/Events/OrderShipped.php und ändern Sie die Klassendefinition wie folgt:
class OrderShipped implements ShouldBroadcast
(4) Die ShouldBroadcast-Schnittstelle erfordert die Implementierung der BroadcastOn-Methode, die verwendet wird, um das Framework zu informieren: an welchen Kanal „dieses Ereignis gesendet werden soll“.
Das Broadcast-System von Laravel ermöglicht die Existenz mehrerer Kanäle. Sie können Benutzernamen verwenden, um verschiedene Kanäle zu unterscheiden, sodass verschiedene Benutzer unterschiedliche Nachrichten von verschiedenen Kanälen erhalten und auch individuell mit verschiedenen Kunden kommunizieren können.
Natürlich können Sie den Kanal beliebig benennen, am besten ist es jedoch, bestimmte Regeln einzuhalten.
Da wir nur den vom Artisan-Befehl generierten Ereigniscode verwendet haben, können Sie die Definition der BroadcastOn-Methode bereits unten in der Datei sehen. Wir modifizieren es leicht:
public function broadcastOn() { return new Channel('orderStatus'); }
Hier benennen wir den Kanal: orderStatus und geben ihn zurück. Das heißt, wenn dieses Ereignis gesendet wird, wird es an den Kanal namens orderStatus gesendet.
Dies ist ein „öffentlicher Kanal“. Jeder kann diesen Kanal überwachen und Broadcast-Nachrichten empfangen. Laravel bietet auch „private Kanäle“, die nach Überprüfung der Berechtigung erfolgreich überwacht werden können. Darüber reden wir später.
(5) Standardmäßig verwendet Laravel den „Ereignisnamen“ als „Nachrichtennamen“ der Übertragung ohne Daten. Wir können beliebige Mitgliedsvariablen innerhalb der Ereignisklasse hinzufügen und den Konstruktor ändern, um Daten an den Client zu senden.
//可添加任意成员变量 public $id; //事件构造函数 public function __construct($id) { $this->id = $id; } //自定义广播的消息名 public function broadcastAs() { return 'anyName'; }
(6) Wie oben haben wir im Grunde den Grundmechanismus der Ausstrahlung etabliert. Als nächstes benötigen wir eine Schnittstelle, die „Ereignisse auslösen“ (also Broadcasts senden) kann.
Fügen Sie den folgenden Code in Routes/api.php hinzu:
Route::get('/ship', function (Request $request) { $id = $request->input('id'); event(new OrderShipped($id)); // 触发事件 return Response::make('Order Shipped!'); });
(7) OK! Öffnen Sie Postman, geben Sie http://***/api/ship?id=1000 ein und senden Sie.
Öffnen Sie storage/logs/laravel.log, Sie werden ein paar weitere Zeilen finden:
[2018-03-02 01:41:19] local.INFO: Broadcasting [App\Events\OrderShipped] on channels [orderStatus] with payload: { "id": "1000", "socket": null }
Herzlichen Glückwunsch, Sie haben den Broadcast-Log-Treiber erfolgreich konfiguriert.
3. Broadcast
Im vorherigen Abschnitt haben wir log als Broadcast-Treiber verwendet, was bedeutet, dass Broadcast-Nachrichten im Protokoll aufgezeichnet werden, also wie es wirklich geht Wie sieht es mit der Kommunikation mit dem Kunden aus? Dies erfordert den Einsatz der eingangs erwähnten „indirekten Schicht“.
(1) Ändern Sie zunächst den Treiber von Stamm auf Schieber.
Um die Schritte zur Installation von Redis zu sparen, verwenden wir das HTTP-Protokoll, um direkt an den kompatiblen „lokalen Pusher-Server“ (dh die indirekte Ebene) zu pushen.
(2) Da Laravel keinen integrierten Broadcast Pusher-Treiber hat, müssen Sie Composer verwenden, um das Pusher PHP SDK zu installieren:
composer require pusher/pusher-php-server
(3) Registrieren Sie AppProvidersBroadcastServiceProvider.
Für Laravel 5.6 müssen Sie nur die entsprechenden Kommentare im Provider-Array in config/app.php auskommentieren.
(4) Als nächstes müssen Sie die „indirekte Schicht“ für die Kommunikation zwischen dem Server und dem Client installieren und konfigurieren.
Wir verwenden die offizielle Dokumentenempfehlung: tlaverdure/laravel-echo-server. Dies ist ein WebSocket-Server, der mit Node.js + Socket.IO implementiert wird.
Es ist mit der Pusher-HTTP-API kompatibel, sodass Sie den Treiber direkt auf Pusher statt auf Redis wie oben ändern können.
npm install -g laravel-echo-server
Initialisieren Sie die Konfigurationsdatei und füllen Sie sie gemäß den Anweisungen aus.
laravel-echo-server init
Öffnen Sie laravel-echo-server.json und prüfen Sie, ob einige wichtige Konfigurationselemente korrekt sind:
"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: 'socket.io', host: window.location.hostname + ':6001' });
接着编写「收听频道」代码,非常简单:
Echo.channel(`orderStatus`) // 广播频道名称 .listen('OrderShipped', (e) => { // 消息名称 console.log(e); // 收到消息进行的操作,参数 e 为所携带的数据 });
(8)安装依赖,编译前端文件。执行如下命令:
npm install npm run dev
最后,在 Blade 模板内引用我们刚刚写好的 JS 脚本。由于 app.js 默认已经引用 bootstrap.js,所以我们只需要引用 app.js 即可。
我们在第(5)步的文件内添加:
<script src="{{ asset('/js/app.js') }}"></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('routes/channels.php'); }
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('orderStatus', function ($user, $value) { return true; // or false });
由于 Broadcast 已经使用 Auth 进行用户登录认证,所以我们只需无条件返回 true 即可实现:任何已登录用户都可以收听此频道。
(5)认证部分我们已经配置完成,接下来将共有频道的定义改为私有。
修改广播消息基于的事件类 app/Events/OrderShipped.php:
public function broadcastOn() { return new PrivateChannel('orderStatus'); // 私有频道 }
修改客户端收听代码 resources/assets/js/bootstrap.js。
Echo.private(`orderStatus`) // 私有频道 .listen('OrderShipped', (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('orderStatus-' . $this->userId); // 动态命名私有频道 }
其次,修改第(4)步中的 routes/channels.php 文件。Broadcast 支持使用通配符匹配「某一类」频道进行验证。
Broadcast::channel('orderStatus-{userId}', 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, ['private-', 'presence-']) && ! $request->user()) { throw new AccessDeniedHttpException; } $channelName = Str::startsWith($request->channel_name, 'private-') ? Str::replaceFirst('private-', '', $request->channel_name) : Str::replaceFirst('presence-', '', $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 ?: ['middleware' => ['web']]; $this->app['router']->group($attributes, function ($router) { $router->post('/broadcasting/auth', '\\'.BroadcastController::class.'@authenticate'); }); }
通过源码可知:此参数等效于 Route::group() 的第一个参数。所以我们只要将其修改为如下形式:
Broadcast::routes(['middleware' => ['yourMiddleware'])
并在中间件内进行用户认证;如未登录,照常抛出 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发送短信
相关课程推荐:
Die neuesten fünf empfohlenen Laravel-Video-Tutorials im Jahr 2017
Das obige ist der detaillierte Inhalt vonWie die integrierte Broadcast-Funktion des Laravel-Frameworks eine Echtzeitkommunikation mit dem Client erreicht. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!