laravel-websockets 實現“低配版”廣播系統

peace42發表於2023-01-04

前言

網上找了好幾個教程都沒成功,然後死磕一個教程不斷研究最終成功了。然後就寫了這篇教程,希望能幫到跟我一樣笨的人。另外希望不要誤導到讀者。希望讀者最好不要過於信任我的這個教程。我其實是稀裡糊塗地成功的,我的這個教程可能也有坑。還有一點是我覺得我研究的過程好像收穫挺大的,但是收穫的某些東西感覺沒法用語言表達出來,如果時間充裕的話建議自己研究。

教程的開頭我先大致介紹一下我是怎麼搞出這個教程介紹的方法的。我建議看教程的人重視一下這部分,因為我懷疑 Laravel 或者相關的庫一更新可能又會出現不相容或者錯亂的情況,然後就又有問題了。到時候可能就需要讀者自行研究了。

為什麼說是大致介紹呢,因為很多地方我感覺都是我瞎貓碰死耗子蒙出來的。另外可能需要一些無法言說的經驗吧。

儘量縮小實驗的物件的規模,找步驟少的教程

剛開始我是照著官方文件中文版做的,後來發現這個實在是太複雜了,可能錯一步就會失敗。而且有的時候連錯誤提示也沒有,就算有錯誤提示不是搜不到就是有太多回答完全不一樣的問題。如果儘量縮小規模的話,應該就能儘量少踩點坑了。

將任務分解得可以獲得子任務成功的反饋

剛開始我是一步一步跟著做,然後最後看是否成功,後來發現這種方法太低效了。然後我發現某些子步驟是可以透過一些方法判斷是否成功的,比如 laravel-websockets 安裝成功之後能開啟那個 dashboard。就是 /laravel-websockets 。另外還有傳送廣播的程式碼如果成功了的話可以在前面說的那個 dashboard 裡看到效果。最後就是全部成功就能在瀏覽器裡看到效果了。所以後來我如果當前階段沒有成功就不會繼續做下去了,繼續研究本階段哪裡錯了。

多打 Log

我是透過打 Log 發現客戶端的 Echo 根本沒有初始化成功的,因為少個引數。之前好像也有個錯誤提示,但是我好像沒看懂。透過在初始化 Echo 的前後分別輸出兩條不一樣的 Log,我發現只有前一條執行了,後一條根本沒執行,所以得出了程式遇到錯誤就停止執行了的結論。

對被實驗的物件儘量熟悉起來

比如我沒搜尋到某條報錯的相關資訊。但是後來發現裡面的某個詞在配置裡出現過,改了一下那個配置就好了。就是這個:“Uncaught Options object must provide a cluster”。不過後來我在網上發現個跟我一樣的方法,沒準我之前看過那個方法,可能是因為樓主說沒用所以我就沒試。

多綜合各方資訊,特別是那些成功的

雖然不一定能拿來就用,甚至可能會產生誤導,但是我相信資訊多一些還是更有助於解決問題的。比如我就在嗶哩嗶哩上搜過“laravel 廣播”看過幾個影片。還搜尋了一些非官方文件的教程。

如果搜錯誤提示沒有用的東西或者搜尋結果太多就看看程式碼

不過我感覺這條有用的機率不大,實在沒招了再用吧。我是透過這個方法發現我在取消註釋程式碼的時候少取消註釋了一行,然後出現了很奇怪的錯誤提示。

就算成功了也不要高興得太早,多練習幾遍

步驟越多越容易出問題,這個廣播系統的步驟是真多。多練習也能多熟悉一點。

儘量選擇更新的教程

我一般在搜尋引擎里加個一年內的條件。

面向搜尋引擎程式設計

感覺這次大概有一半以上的問題都是透過搜尋引擎解決的吧,完全自己解決的部分好像很少。

本體

我這個教程某些方面比較囉嗦,是從建立專案開始的,另外還提到了配置資料庫。水平比較高的讀者可以忽略一些內容。至於高階的讀者應該用不著看我的這種東西。不過我對命令的介紹基本沒有,有需求建議去看下面的英文文章。

我這個是公共頻道的。還有佇列用的是預設的 sync,據說只能用於開發環境。總之我這個教程應該是挺殘廢的,但是跑通了應該就可以以此為起點根據官方文件實驗新的配置和新增新的功能了。

我的這個教程主要參考自這篇文章:How to use Laravel WebSockets

補充

文章建立兩天後發現忘了說安裝 Vite 了。下面是安裝 Vite 的教程:
Laravel Vite 編譯 Assets
有點想在虛擬機器裡來一遍,但是感覺太麻煩了。

建立專案

在 Apache24\htdocs 或者類似的地方執行下面的命令:

composer create-project laravel/laravel bc

在專案路徑中執行以下命令:

php artisan serve

注意執行完上面的命令當前命令列視窗一般就不能執行命令了,需要重啟一個命令列視窗。想要在繼續在當前命令列視窗執行命令除非配合後臺執行的命令。

配置資料庫資訊

在配置資料庫之前需要建立一個資料庫,我用的是 test。不過就算不建立在 migrate 的時候也會提示建立。這個差點就忘了,因為之前一直沒有刪除這個資料庫。

.env

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=test
DB_USERNAME=root
DB_PASSWORD=1234

注意要填你的資料庫的實際的資訊,別照抄我的配置。

安裝伺服器端包 beyondcode/laravel-websockets

在專案路徑中執行以下命令:

composer require beyondcode/laravel-websockets
php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider" --tag="migrations"
php artisan migrate
php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider" --tag="config"

安裝 pusher

在專案路徑中執行以下命令:

composer require pusher/pusher-php-server

注意這條我跟我參考的那篇文章不一樣,我這個安裝的應該是最新版的。原文好像指定版本了。

配置 Laravel WebSockets

.env

BROADCAST_DRIVER=pusher

PUSHER_APP_ID=12345
PUSHER_APP_KEY=ABCDEFG
PUSHER_APP_SECRET=HIJKLMNOP
PUSHER_HOST=127.0.0.1
PUSHER_PORT=6001
PUSHER_SCHEME=http
PUSHER_APP_CLUSTER=mt1

注意這裡跟我參考的那篇英文文章也不一樣,我強迫症,感覺改 .env 更優雅一些。畢竟其他地方都會優先讀 .env。感覺 .env 更像是函式,一個地方改動了不用修改所有呼叫的地方。感覺前一句有點問題,大概就是那個意思吧。

再注意一下第二條到第四條的值是隨便填的。

執行 Laravel WebSockets 伺服器

在專案路徑中執行以下命令:

php artisan websockets:serve

這個也是執行之後當前命令列視窗就不能輸入命令了。

之後在瀏覽器位址列輸入 127.0.0.1:8000/laravel-websockets 就能看前面的操作是否成功了。如果你不是透過“php artisan serve”執行的伺服器的話可能埠會不一樣。點選那個 Connect 按鈕如果在 Events 下面出現一些東西應該就是階段性成功了。注意這個網頁在沒那個啥的情況下開啟是非常慢的,至少在我這裡很慢。因為裡面有個 js 庫的下載速度很慢。注意下面的改 blade 模板的行為是可選的!另外我不知道下面的那個 cdn 有沒有問題,畢竟好像不是大廠的 cdn。如果你嫌慢又不想那個啥的話可以將 vendor\beyondcode\laravel-websockets\resources\views\dashboard.blade.php 中的

<script  src="https://cdn.plot.ly/plotly-latest.min.js"></script>

換成

<script  src="https://cdn.bootcdn.net/ajax/libs/plotly.js/2.17.0/plotly.min.js"></script>

我是透過用 VS Code 搜尋功能直接搜尋 cdn.plot.ly/plotly-latest.min.js 搜出來這個檔案的。另外是在火狐的開發者工具的網路中發現這個 js 庫載入速度慢的。另外這種直接改這種地方的檔案應該是不太優雅。

建立事件

在專案路徑中執行以下命令:

php artisan make:event NewTrade

app\Events\NewTrade.php

<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class NewTrade implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $trade;

    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct($trade)
    {
        $this->trade = $trade;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new Channel('trades');
    }
}

在專案路徑中執行以下命令:

php artisan tinker

執行這條命令後會啟動 Laravel 的互動式直譯器,我理解就是輸入一些語句可以直接執行。另外還有一些其他的實用的命令。這個也是執行之後當前命令列視窗就不能輸入正常的系統命令了,但是這個命令列視窗之後要輸入 php 語句。順便說一下,VS Code 裡的終端在 tinker 裡沒法 Ctrl + V,但是右鍵是貼上的功能。

然後在上面的執行過 php artisan tinker 的那個命令列視窗執行以下命令:

event (new \App\Events\NewTrade('test'))

之後就能在上面提到的那個 127.0.0.1:8000/laravel-websockets 頁面看到發出的訊息了。能看到的話就說明階段性成功了。

安裝客戶端包 laravel-echo

在專案路徑中執行以下命令:

npm install
npm install --save-dev 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: import.meta.env.VITE_PUSHER_APP_KEY,
    wsHost: import.meta.env.VITE_PUSHER_HOST ? import.meta.env.VITE_PUSHER_HOST : `ws-${import.meta.env.VITE_PUSHER_APP_CLUSTER}.pusher.com`,
    wsPort: import.meta.env.VITE_PUSHER_PORT ?? 80,
    wssPort: import.meta.env.VITE_PUSHER_PORT ?? 443,
    forceTLS: (import.meta.env.VITE_PUSHER_SCHEME ?? 'https') === 'https',
    enabledTransports: ['ws', 'wss'],
    cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
});

注意這裡也跟我參考的那篇英文文章不一樣。我只是取消註釋了那個檔案中的那些東西,並在結尾新增了一行“cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,”。

在專案路徑中執行以下命令:

npm run dev

這個也是執行之後當前命令列視窗就不能輸入命令了。

新增客戶端指令碼

在檔案
resources\views\welcome.blade.php
的 head 標籤的結尾新增以下程式碼:

        @vite('resources/js/app.js')
        <script>
            window.onload = function(){
                window.Echo.channel('trades')
                .listen('NewTrade', (e) => {
                    console.log(e.trade);
                });
            };
        </script>
    </head>

注意這裡也跟我參考的那篇英文文章不一樣。改了兩處,一個是匯入 app.js 的方法換了。另外套了一層 window.onload。

然後在瀏覽器的位址列輸入 127.0.0.1:8000/ 啟動開發者工具並切換到控制檯標籤頁。然後再在前面執行過 php artisan tinker 的那個命令列視窗中執行以下命令:

event (new \App\Events\NewTrade('test'))

最後切換到前面提到的開發者工具中的控制檯標籤頁,應該能看到一條“test”訊息。能看到就說明最終成功了。

結語

我只是個菜狗,不要問我太複雜的問題。我從開始搞這個廣播系統到最終成功好像用了兩三天的時間,就憑這個時間你應該就能體會到我有多菜了。

最後因為我是菜狗,所以可能會有一些理解上的錯誤,歡迎指出來。不過按照我的這個教程做基本上應該是會成功的。我做完這個教程自己照著又做了兩遍,沒問題。不過也可能會因為我沒注意到一些東西,或者讀者的環境跟我的不一樣導致讀者不成功。所以我也不敢保證。感覺早晚會失效,失效瞭如果我沒更新的話誰看到了就回復一下提醒一下別人吧。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章