Laravel 中使用 swoole 專案實戰開發案例一 (建立 swoole 和前端通訊)

YxxGoodBoy發表於2019-07-01

最近使用swoole做了專案,裡面設計推送資訊給介面前端,和無登陸使用者的狀態監控,以下是本人從中獲取的一點心得,有改進的地方請留言評論。

1 開發需要環境

工欲善其事,必先利其器。在正式開發之前我們檢查好需要安裝的擴充,不要開發中發現這些問題,打斷思路影響我們的開發效率。

  • 安裝swoole擴充包
  • 安裝redis擴充包
  • 安裝laravel5.5版本以上

2 Laravel 生成命令列

  1. php artisan make:command SwooleDemo

    class SwooleDemo extends Command
    {
    
    protected $signature = 'swoole:demo';
    
    protected $description = '這是關於swoole的一個測試demo';
    
    public function __construct()
    {
        parent::__construct();
    }
    
    public function handle()
    {
        $this->line("hello world");
    }
    }
  2. 我們分別執行php artisan指令和 php artisan swoole:demo會看到關於這個命令的說明,和輸出hello world。(laravel命令列用法詳解)

3 命令列邏輯程式碼

  • 編寫一個最基礎的swoole命令列邏輯程式碼
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;

class SwooleDemo extends Command
{
    // 命令名稱
    protected $signature = 'swoole:demo';
    // 命令說明
    protected $description = '這是關於swoole websocket的一個測試demo';
    // swoole websocket服務
    private static $server = null;

    public function __construct()
    {
        parent::__construct();
    }

    // 入口
    public function handle()
    {
        $this->redis = Redis::connection('websocket');
        $server = self::getWebSocketServer();
        $server->on('open',[$this,'onOpen']);
        $server->on('message', [$this, 'onMessage']);
        $server->on('close', [$this, 'onClose']);
        $server->on('request', [$this, 'onRequest']);
        $this->line("swoole服務啟動成功 ...");
        $server->start();
    }

    // 獲取服務
    public static function getWebSocketServer()
    {
        if (!(self::$server instanceof \swoole_websocket_server)) {
            self::setWebSocketServer();
        }
        return self::$server;
    }
    // 服務處始設定
    protected static  function setWebSocketServer():void
    {
        self::$server  = new \swoole_websocket_server("0.0.0.0", 9502);
        self::$server->set([
            'worker_num' => 1,
            'heartbeat_check_interval' => 60,    // 60秒檢測一次
            'heartbeat_idle_time' => 121,        // 121秒沒活動的
        ]);
    }

    // 開啟swoole websocket服務回撥程式碼
    public function onOpen($server, $request)
    {
        if ($this->checkAccess($server, $request)) {\
            self::$server->push($request->fd,"開啟swoole服務成功!");\
        }
    }
    // 給swoole websocket 傳送訊息回撥程式碼
    public function onMessage($server, $frame)
    {

    }
    // http請求swoole websocket 回撥程式碼
    public function onRequest($request,$response)
    {

    }
    // websocket 關閉回撥程式碼
    public function onClose($serv,$fd)
    {
        $this->line("客戶端 {$fd} 關閉");
    }
    // 校驗客戶端連線的合法性,無效的連線不允許連線
    public function checkAccess($server, $request):bool
    {
        $bRes = true;
        if (!isset($request->get) || !isset($request->get['token'])) {
            self::$server->close($request->fd);
            $this->line("介面驗證欄位不全");
            $bRes = false;
        } else if ($request->get['token'] !== "123456") {
            $this->line("介面驗證錯誤");
            $bRes = false;
        }
        return $bRes;
    }
    // 啟動websocket服務
    public function start()
    {
        self::$server->start();
    }

}
  • 編寫websoket js程式碼

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>swoole測試</title>
    <meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
</head>
<body>
<h1>這是一個測試</h1>
</body>
<script>
    var ws;//websocket例項
    var lockReconnect = false;//避免重複連線
    var wsUrl = 'ws://{{$_SERVER["HTTP_HOST"]}}:9502?page=home&token=123456';

    function initEventHandle() {
        ws.onclose = function () {
            reconnect(wsUrl);
        };
        ws.onerror = function () {
            reconnect(wsUrl);
        };
        ws.onopen = function () {
            //心跳檢測重置
            heartCheck.reset().start();
        };
        ws.onmessage = function (event) {
            //如果獲取到訊息,心跳檢測重置
            //拿到任何訊息都說明當前連線是正常的
            var data = JSON.parse(event.data);
            heartCheck.reset().start();
        }
    }
    createWebSocket(wsUrl);
    /**
     * 建立連結
     * @param url
     */
    function createWebSocket(url) {
        try {
            ws = new WebSocket(url);
            initEventHandle();
        } catch (e) {
            reconnect(url);
        }
    }
    function reconnect(url) {
        if(lockReconnect) return;
        lockReconnect = true;
        //沒連線上會一直重連,設定延遲避免請求過多
        setTimeout(function () {
            createWebSocket(url);
            lockReconnect = false;
        }, 2000);
    }
    //心跳檢測
    var heartCheck = {
        timeout: 60000,//60秒
        timeoutObj: null,
        serverTimeoutObj: null,
        reset: function(){
            clearTimeout(this.timeoutObj);
            clearTimeout(this.serverTimeoutObj);
            return this;
        },
        start: function(){
            var self = this;
            this.timeoutObj = setTimeout(function(){
                //這裡傳送一個心跳,後端收到後,返回一個心跳訊息,
                //onmessage拿到返回的心跳就說明連線正常
                ws.send("heartbeat");
                self.serverTimeoutObj = setTimeout(function(){//如果超過一定時間還沒重置,說明後端主動斷開了
                    ws.close();//如果onclose會執行reconnect,我們執行ws.close()就行了.如果直接執行reconnect 會觸發onclose導致重連兩次
                }, self.timeout);
            }, this.timeout);
        },
        header:function(url) {
            window.location.href=url
        }

    }
</script>
</html>
  • 訪問前端頁面(顯示如下說明前後端連結成功)

Swoole

相關文章