在 Laravel6 中使用 基于Workerman 的Gateway-worker进行 socket 长连接通讯

605 阅读3分钟

本人已参与【新人创作礼】活动,一起开启掘金创作之路。

一、说明

本人最近是因为公司的一个互联互通的项目里面的考试系统模块需要用到socket长连接通讯,才写的这篇技术文档,同时也是为了以后再遇到这类问题的时候,可以更快速,更方便的去实现功能,也希望同行的技术伙伴如果有幸被你看到我写的这篇《在laravel6中使用基于Workerman的Gateway-worker进行socket长连接通讯》。

二、简介

GatewayWorker基于 Workerman开发的一个项目框架,用于快速开发 TCP长连接应用,例如app推送服务端、即时IM服务端、游戏服务端、物联网、智能家居等等

GatewayWorker使用经典的GatewayWorker进程模型。Gateway进程负责维持客户端连接,并转发客户端的数据给BusinessWorker进程处理,BusinessWorker进程负责处理实际的业务逻辑(默认调用Events.php处理业务),并将结果推送给对应的客户端。Gateway服务和BusinessWorker服务可以分开部署在不同的服务器上,实现分布式集群。

GatewayWorker提供非常方便的API,可以全局广播数据、可以向某个群体广播数据、也可以向某个特定客户端推送数据。配合Workerman的定时器,也可以定时推送数据。

三、安装gateway-worker

composer require workerman/gateway-worker
composer require workerman/gatewayclient

四、创建 Workerman 启动文件

创建一个 artisan 命令行工具来启动 Socket服务端,在 app/Console/Commands 目录下建立命令行文件。

php artisan make:command GatewayWorkerServer

<?php
 
namespace App\Console\Commands;
 
use Illuminate\Console\Command;
use GatewayWorker\BusinessWorker;
use GatewayWorker\Gateway;
use GatewayWorker\Register;
use Workerman\Worker;
 
 
class GatewayWorkerServer extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'workman {action} {--d}';
 
    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Start a Workerman server.';
 
    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }
 
    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        //
        global $argv;
        $action = $this->argument('action');
 
        $argv[0] = 'artisan workman';
        $argv[1] = $action;
        $argv[2] = $this->option('d') ? '-d' : '';   //必须是一个-,上面定义命令两个--,后台启动用两个--
 
        $this->start();
    }
 
    private function start()
    {
        $this->startGateWay();
        $this->startBusinessWorker();
        $this->startRegister();
        Worker::runAll();
    }
 
    private function startBusinessWorker()
    {
        $worker                  = new BusinessWorker();
        $worker->name            = 'BusinessWorker';
        $worker->count           = 1;
        $worker->registerAddress = '127.0.0.1:1236';
        $worker->eventHandler    = \App\GatewayWorker\Events::class;
    }
 
    private function startGateWay()
    {
        $gateway = new Gateway("websocket://0.0.0.0:2346");
        $gateway->name                 = 'Gateway';
        $gateway->count                = 1;
        $gateway->lanIp                = '127.0.0.1';
        $gateway->startPort            = 2300;
        $gateway->pingInterval         = 30;
        $gateway->pingNotResponseLimit = 0;
        $gateway->pingData             = '{"type":"ping"}';
        $gateway->registerAddress      = '127.0.0.1:1236';
    }
 
    private function startRegister()
    {
        new Register('text://0.0.0.0:1236');
    }
 
       //php artisan workman start --d  之后    打开浏览器F12 将内容复制到console里return就行
       /* ws = new WebSocket("ws://*.*.*.*:2346");
        ws.onopen = function() {
            ws . send('{"name":"one","user_id":"111"}');
            ws . send('{"name":"two","user_id":"222"}');
        };
        ws.onmessage = function(e) {
            console.log("收到服务端的消息:" + e.data);
        };
        ws.onclose = function(e) {
            console.log("服务已断开" );
        };*/
 
}

五、创建事件监听文件

创建一个 app/GatewayWorker/Events.php 文件来监听处理 workman 的各种事件。

<?php
/**
 * Created by PhpStorm.
 * User: xing
 * Date: 2022/2/20
 * Time: 16:09
 */
namespace App\GatewayWorker;
 
use GatewayWorker\Lib\Gateway;
use Illuminate\Support\Facades\Log;
 
class Events
{
 
    public static function onWorkerStart($businessWorker)
    {
        echo "onWorkerStart\r\n";
    }
 
    public static function onConnect($client_id)
    {
        Gateway::sendToClient($client_id, json_encode(['type' => 'onConnect', 'client_id' => $client_id]));
        echo "onConnect\r\n";
    }
 
    public static function onWebSocketConnect($client_id, $data)
    {
        echo "onWebSocketConnect\r\n";
    }
 
    public static function onMessage($client_id, $message)
    {
        Gateway::sendToClient($client_id, json_encode(['type' => 'onMessage', 'client_id' => $client_id, 'name' => json_decode($message)->name]));
 
        echo "onMessage\r\n";
    }
 
    public static function onClose($client_id)
    {
        Log::info('Workerman close connection' . $client_id);
        echo "onClose\r\n";
    }
 
}

六、测试。 启动 Workerman 服务端

在命令行里面执行,支持的命令大概有 start|stop|restart,其中 --d 的意思是 daemon 模式,后台守护进程。

php artisan workman start --d //正式环境开发命令
php artisan workman start  //测试环境开发命令

ls@ls-virtual-machine:/var/www/html/socket/socket$ php artisan workman start --d
Workerman[artisan workman] start in DAEMON mode
-------------------------------------------- WORKERMAN ---------------------------------------------
Workerman version:4.0.2          PHP version:7.2.24-0ubuntu0.18.04.3
--------------------------------------------- WORKERS ----------------------------------------------
proto   user            worker            listen                      processes    status           
tcp     ls              Gateway           websocket://0.0.0.0:2346    1             [OK]            
tcp     ls              BusinessWorker    none                        1             [OK]            
tcp     ls              Register          text://0.0.0.0:1236         1             [OK]            
----------------------------------------------------------------------------------------------------
Input "php artisan workman stop" to stop. Start success.

在浏览器 F12 打开调试模式,在 Console 里输入

ws = new WebSocket("ws://*.*.*.*:2346");
    ws.onopen = function() {
        ws . send('{"name":"one","user_id":"111"}');
        ws . send('{"name":"two","user_id":"222"}');
    };
    ws.onmessage = function(e) {
        console.log("收到服务端的消息:" + e.data);
    };
    ws.onclose = function(e) {
        console.log("服务已断开" );
    }

使用swoole连接WebSocket时报错: WebSocket connection to ‘ws://*.*.*.*:8506/’ failed: Error in connection establish

请运行以下代码:
关闭防火墙:

systemctl stop firewalld.service

关闭后客户端出现如下图则说明连接成功。

具体功能的实现需要借助官方手册就可以实现轻松的完成

官方手册:与ThinkPHP等框架结合-gateway-worker手册