Laravel 結合 GatewayWorker 推送消息

 

一、簡述

GatewayWorker基於Workerman開發的一個項目框架,用於快速開發TCP長連接應用,例如app推送服務端、即時IM服務端、遊戲服務端、物聯網、智能家居等等

GatewayWorker使用經典的GatewayWorker進程模型。Gateway進程負責維持客戶端連接,並轉發客戶端的數據給BusinessWorker進程處理,BusinessWorker進程負責處理實際的業務邏輯(默認調用Events.php處理業務),並將結果推送給對應的客戶端。Gateway服務和BusinessWorker服務可以分開部署在不同的服務器上,實現分佈式集羣。

GatewayWorker提供非常方便的API,可以全局廣播數據、可以向某個羣體廣播數據、也可以向某個特定客戶端推送數據。配合Workerman的定時器,也可以定時推送數據。

二、安裝

GatewayWorker 安裝在LInux服務器上:

下載地址:GatewayWorker

1、下載 demo

2、命令行運行 unzip GatewayWorker.zip 解壓縮GatewayWorker.zip

3、命令行運行 cd GatewayWorker 進入GatewayWorker目錄

4、命令行運行 php start.php start 啓動 GatewayWorker

5、新開幾個命令行窗口運行 telnet 127.0.0.1 8282,輸入任意字符即可聊天(非本機測試請將127.0.0.1替換成實際ip)。

注意:如果telnet超時請檢查服務器防火牆(iptables)

這裏啓動的是GatewayWorker的默認端口,要是需要更改要改以下三個文件:
GatewayWorker/Applications/YourApp/start_register.php

// register 必須是text協議;這個端口要保持一致,根據項目自定義
$register = new Register('text://0.0.0.0:1238');

GatewayWorker/Applications/YourApp/start_businessworker.php

// bussinessWorker 進程
$worker = new BusinessWorker();
// worker名稱,根據不同的項目自定義
$worker->name = 'YourAppBusinessWorker';
// bussinessWorker進程數量
$worker->count = 4;
// 服務註冊地址; 注意這個端口要保持一致,不同的項目可以自定義
$worker->registerAddress = '127.0.0.1:1238';

GatewayWorker/Applications/YourApp/start_gateway.php

// gateway 進程,這裏使用Text協議,可以用telnet測試
$gateway = new Gateway("tcp://0.0.0.0:8282");
// gateway名稱,status方便查看
$gateway->name = 'YourAppGateway';
// gateway進程數
$gateway->count = 4;
// 本機ip,分佈式部署時使用內網ip
$gateway->lanIp = '127.0.0.1';
// 內部通訊起始端口,假如$gateway->count=4,起始端口爲4000
// 則一般會使用4000 4001 4002 4003 4個端口作爲內部通訊端口 ,不同項目開啓不同端口
$gateway->startPort = 2900;
// 服務註冊地址;端口保持一致,項目自定義
$gateway->registerAddress = '127.0.0.1:1238';

// 服務端向客戶端發送心跳數據的時間間隔 單位:秒。如果設置爲0代表不發送心跳檢測
//$gateway->pingInterval = 10;
//客戶端連續$pingNotResponseLimit次$pingInterval時間內不迴應心跳則斷開鏈接。
//如果設置爲0代表客戶端不用發送迴應數據,即通過TCP層面檢測連接的連通性(極端情況至少10分鐘才能檢測到)
//$gateway->pingNotResponseLimit = 2;
// 要發送的心跳請求數據,心跳數據是任意的,只要客戶端能識別即可
//$gateway->pingData = '{"type":"ping"}';
注意:需要客戶端與服務端保持連接不被斷開,可以開啓心跳檢查定時發送心跳數據。

服務管理

GatewayWorker根目錄下有一個start.php文件:

php start.php  start   啓動服務 (php start.php  start  -d   以daemon啓動)

php start.php  stop    停止服務

php start.php restart  重新啓動 (-d 以daemon啓動)

php start.php reload   平滑重啓

php start.php  status  服務狀態

vagrant@homestead:~/code/GatewayWorker$ php start.php status

Workerman[start.php] status 
----------------------------------------------GLOBAL STATUS----------------------------------------------------
Workerman version:3.5.10          PHP version:7.2.3-1+ubuntu16.04.1+deb.sury.org+1
start time:2018-06-08 14:00:54   run 0 days 2 hours   
load average: 0, 0, 0            event-loop:\Workerman\Events\Select
3 workers       9 processes
worker_name       exit_status      exit_count
YourAppBusinessWorker 0                0
YourAppGateway        0                0
Register          0                0
----------------------------------------------PROCESS STATUS---------------------------------------------------
pid    memory  listening             worker_name       connections send_fail timers  total_request qps    status
4693    2M      none                YourAppBusinessWorker 5           0         0       3             0      [idle]
4694    2M      none                YourAppBusinessWorker 5           0         0       3             0      [idle]
4695    2M      none                YourAppBusinessWorker 5           0         0       156           0      [idle]
4696    2M      none                YourAppBusinessWorker 5           0         0       3             0      [idle]
4697    2M      tcp://0.0.0.0:8282  YourAppGateway        8           0         0       226           0      [idle]
4698    2M      tcp://0.0.0.0:8282  YourAppGateway        8           0         0       226           0      [idle]
4699    2M      tcp://0.0.0.0:8282  YourAppGateway        8           0         0       226           0      [idle]
4700    2M      tcp://0.0.0.0:8282  YourAppGateway        8           0         0       378           0      [idle]
4701    2M      text://0.0.0.0:1238     Register          8           0         0       51            0      [idle]
----------------------------------------------PROCESS STATUS---------------------------------------------------
Summary    18M     -                   -                 60          0         0       1272          0      [Summary]
注意:修改代碼要重啓服務。

GatewayClient安裝下項目中

原則:
現有mvc框架項目與GatewayWorker獨立部署互不干擾

所有的業務邏輯都由網站頁面post/getmvc框架中完成

GatewayWorker不接受客戶端發來的數據,即GatewayWorker不處理任何業務邏輯,GatewayWorker僅僅當做一個單向的推送通道

僅當mvc框架需要向瀏覽器主動推送數據時纔在mvc框架中調用GatewayAPI(GatewayClient)完成推送

當然,這只是官方推薦的結合方式,也不是絕對的。開發者可以自由變化選擇結合方式以適應自己的業務需求。 當然也可以採用客戶端與GatewayWorker直接雙向通訊的方式完成業務通訊。更多參考官方手冊

下載地址:GatewayClient。項目中執行以下命令即可:

composer require workerman/gatewayclient

三、應用

laravel中結合redis 消息隊列(可參考laravel/lumen 使用 redis隊列)來完成消息推送。

controller中使用dispatch觸發隊列

dispatch(new YourJob($data));

Jobs中示例:

<?php

namespace App\Jobs;


use GatewayClient\Gateway;

class YourJob extends Job
{
    /**
     * Create a new job instance.
     *
     * @return void
     */
    protected $id;


    public function __construct($id) 
    {
        $this->id = $id;
    }

    public function handle()
    {
        //業務邏輯
        Gateway::sendToAll(json_encode(['id' => xxx, 'name' => xxx]));
    }
}
每次改動代碼重啓隊列。supervisorctl reload

四、調試

服務器端打開Telnet,不可執行Telnet命令需要安裝。
如:

vagrant@homestead:~/code/GatewayWorker$ telnet 127.0.0.1 8282
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
Hello 7f0000010b5600000001
7f0000010b5600000001 login
hello
7f0000010b5600000001 said hello

{"id":"35","name":"xxx"}

正常情況,會輸出你想發送的消息。
Escape character is '^]'.是 Ctrl + ] 調出Telnet,輸入quit可以退出。

五、配置wss

Workerman如何創建一個wss服務,使得客戶端可以用過wss協來連接通訊,比如在微信小程序中連接服務端。

wss協議實際是websocket+SSL,就是在websocket協議上加入SSL層,類似https(http+SSL)。 所以只需要在websocket協議的基礎上開啓SSL即可支持wss協議。
  • 配置方式一:直接用Workerman開啓SSL:

準備工作:

1、Workerman版本不小於3.3.7

2、PHP安裝了openssl擴展

3、已經申請了證書(pem/crt文件及key文件)放在磁盤任意目錄

start_gateway.php中設置以下代碼

<?php
require_once __DIR__ . '/Workerman/Autoloader.php';
use Workerman\Worker;

// 證書最好是申請的證書
$context = array(
    // 更多ssl選項請參考手冊 http://php.net/manual/zh/context.ssl.php
    'ssl' => array(
        // 請使用絕對路徑
        'local_cert'                 => '磁盤路徑/server.pem', // 也可以是crt文件
        'local_pk'                   => '磁盤路徑/server.key',
        'verify_peer'                => false,
        // 'allow_self_signed' => true, //如果是自簽名證書需要開啓此選項
    )
);
// 這裏設置的是websocket協議(端口任意,但是需要保證沒被其它程序佔用)
$worker = new Worker('websocket://0.0.0.0:443', $context);
$worker->name = xxx //配置名稱便於監控
// 設置transport開啓ssl,websocket+ssl即wss
$worker->transport = 'ssl';
$worker->onMessage = function($con, $msg) {
    $con->send('ok');
};

Worker::runAll();

通過以上的代碼,Workerman就監聽了wss協議,客戶端就可以通過wss協議來連接workerman實現安全即時通訊了。

測試
打開chrome瀏覽器,按F12打開調試控制檯,在Console一欄輸入(或者把下面代碼放入到html頁面用js運行)

// 證書是會檢查域名的,請使用域名連接

ws = new WebSocket("wss://域名");
ws.onopen = function() {
    alert("連接成功");
    ws.send('tom');
    alert("給服務端發送一個字符串:tom");
};
ws.onmessage = function(e) {
    alert("收到服務端的消息:" + e.data);
};
注意:

1、如果無法啓動,則一般是443端口被佔用,請改成其它端口,注意改成其它端口後客戶端連接時需要帶上端口號,客戶端連接時地址類似wss://domain.com:xxx ,xxx爲端口號。如果必須使用443端口請使用方法二代理的方式實現wss

2、wss端口只能通過wss協議訪問,ws無法訪問wss端口。

3、證書一般是與域名綁定的,所以測試的時候客戶端請使用域名連接,不要使用ip去連。

4、如果出現無法訪問的情況,請檢查服務器防火牆。

5、此方法要求PHP版本>=5.6,因爲微信小程序要求tls1.2,而PHP5.6以下版本不支持tls1.2。

6、微信小程序要求連接wss時不帶端口號,也就是wss端口只能是443。但是443端口一般被nginx/apache佔用,此時可以考慮方法二。

  • 配置方式二:利用nginx/apache代理wss
利用nginx/apache作爲wss代理轉發給workerman注意此方法workerman部分千萬不要設置ssl,否則將無法連接)。
通訊原理及流程是:

1、客戶端發起wss連接連到nginx/apache

2、nginx/apachewss協議的數據轉換成ws協議數據並轉發到Workermanwebsocket協議端口

3、Workerman收到數據後做業務邏輯處理

4、Workerman給客戶端發送消息時,則是相反的過程,數據經過nginx/apache轉換成wss協議然後發給客戶端

nginx配置參考

前提條件及準備工作:

1、假設Workerman監聽的是8282端口(websocket協議,不要用tcp)
2、已經申請了證書(pem/crt文件及key文件)放在了/etc/nginx/conf.d/ssl
3、打算利用nginx開啓443端口對外提供wss代理服務(端口可以根據需要修改)
4、nginx一般作爲網站服務器運行着其它服務,爲了不影響原來的站點使用,這裏使用地址 域名/wss 作爲wss的代理入口。也就是客戶端連接地址爲 wss://域名/wss

nginx配置類似如下:

server {
  listen 443;

  ssl on;
  ssl_certificate /etc/ssl/server.pem;
  ssl_certificate_key /etc/ssl/server.key;
  ssl_session_timeout 5m;
  ssl_session_cache shared:SSL:50m;
  ssl_protocols SSLv3 SSLv2 TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;

  location /wss
  {
    proxy_pass http://127.0.0.1:8282;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header X-Real-IP $remote_addr;
  }

  # location / {} 站點的其它配置...
}

測試

// 證書是會檢查域名的,請使用域名連接

ws = new WebSocket("wss://域名/wss");

ws.onopen = function() {
    alert("連接成功");
    ws.send('tom');
    alert("給服務端發送一個字符串:tom");
};
ws.onmessage = function(e) {
    alert("收到服務端的消息:" + e.data);
};

轉載:https://segmentfault.com/a/1190000015230002

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章