Laravel Reverb 廣播使用

安裝laravel

composer create-project --prefer-dist laravel/laravel laravel-reverb-chat
cd laravel-reverb-chat

配置.env

cp .env.example  .env
vim .env
php artisan install:broadcasting
npm install --save laravel-echo pusher-js

查看配置文件已生成

# .env
REVERB_APP_ID=836301
REVERB_APP_KEY=o2kwvwhasspm4ek1u
REVERB_APP_SECRET=krwrs2wk4oyxpsh
REVERB_HOST="localhost"
REVERB_PORT=8080
REVERB_SCHEME=http

VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"

房間表生成遷移

php artisan make:model Room --migration

編輯 database/migrations

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('rooms', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('rooms');
    }
};

執(zhí)行遷移

php artisan migrate

生成數(shù)據(jù)

php artisan make:seeder RoomsTableSeeder 
<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class RoomsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        DB::table('rooms')->insert([
            ['name' => 'Room 1'],
            ['name' => 'Room 2'],
            ['name' => 'Room 3'],
            ['name' => 'Room 4'],
            ['name' => 'Room 5'],
        ]);
    }
}

執(zhí)行

php artisan db:seed

創(chuàng)建 app/Events/MessageSent.php

<?php

namespace App\Events;

use App\Models\ChatMessage;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\Channel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class MessageSent implements ShouldBroadcastNow
{
    use Dispatchable;
    use InteractsWithSockets;
    use SerializesModels;

    public $userName;
    public $roomId;
    public $message;

    public function __construct($userName, $roomId, $message)
    {
        $this->userName = $userName;
        $this->roomId = $roomId;
        $this->message = $message;
    }

    public function broadcastOn() : Channel
    {

        return new Channel('chat.' . $this->roomId);
    }

    public function broadcastWith()
    {
        return [
            'userName' => $this->userName,
            'message' => $this->message,
        ];
    }
}

創(chuàng)建 resources/views/rooms/index.blade.php

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Chat Rooms</title>
</head>
<body>
<div id="app">
    <h1>Chat Rooms</h1>
    <ul>
        @foreach($rooms as $room)
            <li>
                <a href="{{ route('rooms.show', $room->id) }}">Join {{ $room->name }}</a>
            </li>
        @endforeach
    </ul>
</div>
</body>
</html>

創(chuàng)建 resources/views/rooms/chat.blade.php

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Chat Room: {{ $room->name }}</title>
    @vite(['resources/css/app.css'])
    @vite(['resources/js/app.js'])
</head>
<body>
<div id="app">
    <h2>Chat Room: {{ $room->name }}</h2>
    <div id="messages"
         style="border: 1px solid #ccc; margin-bottom: 10px; padding: 10px; height: 300px; overflow-y: scroll;">
        <!-- Messages will be displayed here -->
    </div>
    <input type="text" id="messageInput" placeholder="Type your message here..." autofocus>
    <button onclick="sendMessage()">Send</button>
</div>

<script>
    document.addEventListener('DOMContentLoaded', function () {
        const roomId = "{{ $room->id }}";
        Echo.channel(`chat.${roomId}`)
            .listen('MessageSent', (e) => {
                const messages = document.getElementById('messages');
                const messageElement = document.createElement('div');
                messageElement.innerHTML = `<strong>${e.userName}:</strong> ${e.message}`;
                messages.appendChild(messageElement);
                messages.scrollTop = messages.scrollHeight; // Scroll to the bottom
            });
    })

    function sendMessage() {
        const messageInput = document.getElementById('messageInput');
        const message = messageInput.value;
        messageInput.value = ''; // Clear input
        const roomId = "{{$room->id}}"
        fetch(`/rooms/${roomId}/message`, {
            method: 'POST',
            headers: {
                'X-CSRF-TOKEN': '{{ csrf_token() }}',
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({message: message})
        }).catch(error => console.error('Error:', error));
    }

</script>
</body>
</html>

修改 resources/js/app.js

import './bootstrap';
import Echo from 'laravel-echo';

import Pusher from 'pusher-js';
window.Pusher = Pusher;

window.Echo = new Echo({
    broadcaster: 'reverb',
    key: import.meta.env.VITE_REVERB_APP_KEY,
    wsHost: import.meta.env.VITE_REVERB_HOST,
    wsPort: import.meta.env.VITE_REVERB_PORT ?? 80,
    wssPort: import.meta.env.VITE_REVERB_PORT ?? 443,
    forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
    enabledTransports: ['ws', 'wss'],
});

生成控制器 php artisan make:controller RoomsController

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Room;

class RoomsController extends Controller
{
    public function index()
    {
        $rooms = Room::all();
        return view('rooms.index',[
            'rooms' => $rooms
        ]);
    }

    public function show(Room $room)
    {
        return view('rooms.chat', [
            'roomId' => $room->id,
            'room' => $room,
            'messages' => []
        ]);
    }
}

生成控制器 php artisan make:controller ChatController

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Str;
use App\Events\MessageSent;

class ChatController extends Controller
{
    public function postMessage(Request $request, $roomId)
    {
        $userName = 'User_' . Str::random(4);
        $messageContent = $request->input('message');
        MessageSent::dispatch($userName, $roomId, $messageContent);
        return response()->json(['status' => 'Message sent successfully.']);
    }
}

routes/web.php 文件

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\RoomsController;
use App\Http\Controllers\ChatController;

Route::get('/', function () {
    return view('welcome');
});


Route::get('/rooms', [RoomsController::class, 'index'])->name('rooms.index');
Route::get('/rooms/{room}', [RoomsController::class, 'show'])->name('rooms.show');
Route::post('/rooms/{roomId}/message', [ChatController::class, 'postMessage'])->name('api.rooms.message.post');

運(yùn)行項目

php artisan serve
npm run dev
php artisan queue:listen
php artisan reverb:start
# 可以設(shè)置特定的主機(jī)或端口,默認(rèn)8080
php artisan reverb:start --host=127.0.0.1 --port=9000

訪問 http://127.0.0.1:8000/rooms

# 后臺運(yùn)行
nohup php artisan queue:listen > queue_listen.log 2>&1 &
nohup php artisan reverb:start > reverb_start_default.log 2>&1 &
nohup php artisan reverb:start --host=127.0.0.1 --port=9000 > reverb_start_9000.log 2>&1 &

# 找到正在運(yùn)行的進(jìn)程
ps -ef | grep "php artisan queue:listen"
ps -ef | grep "php artisan reverb:start"
ps -ef | grep "php artisan reverb:start --host=127.0.0.1 --port=9000"

# 重啟 queue:listen 命令
php artisan queue:restart

# 重啟 reverb:start 命令薄声,使用默認(rèn)端口
php artisan reverb:restart

在https域名下配置, 本地生成證書可以使用 https://github.com/FiloSottile/mkcert

# nginx 代理
 location /app {  # WebSocket 路徑
        proxy_pass http://0.0.0.0:8080;   # Laravel Reverb 運(yùn)行的地址
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

# .env
REVERB_SERVER_HOST=0.0.0.0
REVERB_SERVER_PORT=8080

REVERB_HOST="xxxxx.com"
REVERB_SCHEME=https
REVERB_PORT=443

本地開發(fā)如果報錯cURL error 60: SSL certificate problem: 找到修改為false

/www/wwwroot/demo/chat/laravel-reverb-chat/vendor/guzzlehttp/guzzle/src/Client.php

'verify' => false,
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末镜会,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子绪钥,更是在濱河造成了極大的恐慌捷凄,老刑警劉巖垄懂,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異拟赊,居然都是意外死亡刺桃,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門要门,熙熙樓的掌柜王于貴愁眉苦臉地迎上來虏肾,“玉大人,你說我怎么就攤上這事欢搜》夂溃” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵炒瘟,是天一觀的道長吹埠。 經(jīng)常有香客問我,道長疮装,這世上最難降的妖魔是什么缘琅? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮廓推,結(jié)果婚禮上刷袍,老公的妹妹穿的比我還像新娘。我一直安慰自己樊展,他們只是感情好呻纹,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著专缠,像睡著了一般雷酪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涝婉,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天哥力,我揣著相機(jī)與錄音,去河邊找鬼墩弯。 笑死吩跋,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的渔工。 我是一名探鬼主播锌钮,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼涨缚!你這毒婦竟也來了轧粟?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤脓魏,失蹤者是張志新(化名)和其女友劉穎兰吟,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體茂翔,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡混蔼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了珊燎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惭嚣。...
    茶點(diǎn)故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖悔政,靈堂內(nèi)的尸體忽然破棺而出晚吞,到底是詐尸還是另有隱情,我是刑警寧澤谋国,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布槽地,位于F島的核電站,受9級特大地震影響芦瘾,放射性物質(zhì)發(fā)生泄漏捌蚊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一近弟、第九天 我趴在偏房一處隱蔽的房頂上張望缅糟。 院中可真熱鬧,春花似錦祷愉、人聲如沸窗宦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽迫摔。三九已至,卻和暖如春泥从,著一層夾襖步出監(jiān)牢的瞬間句占,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工躯嫉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留纱烘,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓祈餐,卻偏偏與公主長得像擂啥,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子帆阳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容