在 Laravel 中使用 Workerman 进行 socket 通讯

1.安装 Workerman

由于要使用客户端点对点通讯,选择了 workerman/gateway-worker 的扩展包,它已经引入了 workerman/workerman

$ composer require workerman/gateway-worker

2.创建 Workerman 启动文件

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

<?php

namespace App\Console\Commands;

use GatewayWorker\BusinessWorker;
use GatewayWorker\Gateway;
use GatewayWorker\Register;
use Illuminate\Console\Command;
use Workerman\Worker;

class WorkermanCommand extends Command
{

    protected $signature = 'workman {action} {--d}';

    protected $description = 'Start a Workerman server.';

    public function handle()
    {
        global $argv;
        $action = $this->argument('action');

        $argv[0] = 'wk';
        $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\Workerman\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":"@heart@"}';
        $gateway->registerAddress      = '127.0.0.1:1236';
    }

    private function startRegister()
    {
        new Register('text://0.0.0.0:1236');
    }
}

3.创建事件监听文件

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

<?php

namespace App\Workerman;

class Events
{

    public static function onWorkerStart($businessWorker)
    {
    }

    public static function onConnect($client_id)
    {
    }

    public static function onWebSocketConnect($client_id, $data)
    {
    }

    public static function onMessage($client_id, $message)
    {
    }

    public static function onClose($client_id)
    {
    }
}

4.启动 Workerman 服务端

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

$ php artisan workman start -d

当你看到如下结果的时候,workman已经启动成功了。

Workerman[wk] start in DEBUG mode
----------------------- WORKERMAN -----------------------------
Workerman version:3.5.11          PHP version:7.1.11
------------------------ WORKERS -------------------------------
user          worker          listen                    processes status
root          Gateway         websocket://0.0.0.0:2346   1         [OK]
root          BusinessWorker  none                       1         [OK]
root          Register        text://0.0.0.0:1236        1         [OK]
----------------------------------------------------------------
Press Ctrl+C to stop. Start success.
onWorkerStart

表述能力不是很好,按照这个流程,可以很快的搭建起基于 workerman 的 socket 服务端,其中的运行配置参数等,大家自行百度吧。

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 5年前 自动加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 80

针对 Windows 下无法启动的问题,参考:windows操作系统下如何初始化多个Worker,做了一点特殊处理,也可以自己改进

1、命令行文件,改动

    protected $signature = 'workerman
                            {action : action}
                            {--start=all : start}
                            {--d : daemon mode}';

    public function handle() {
        global $argv;
        $action = $this->argument('action');
        /**
         * 针对 Windows 一次执行,无法注册多个协议的特殊处理
         */
        if ($action === 'single') {
            $start = $this->option('start');
            if ($start === 'register') {
                $this->startRegister();
            } elseif ($start === 'gateway') {
                $this->startGateWay();
            } elseif ($start === 'worker') {
                $this->startBusinessWorker();
            }
            Worker::runAll();

            return;
        }

        /**
         * argv[0] 默认是,当前文件,可以不修改
         */
        //$argv[0] = 'wk';
        $argv[1] = $action;
        // 控制是否进入 daemon 模式
        $argv[2] = $this->option('d') ? '-d' : '';

        $this->start();
    }

2、项目根目录,新建 start_for_win.bat

start /b php artisan workerman single --start=register
start /b php artisan workerman single --start=gateway
start /b php artisan workerman single --start=worker
pause

执行效果截图:

file

注:生产环境还是用 Linux 吧

4年前 评论
Wall_E 2年前
思远 4年前
bing 3年前

Waring: App\Workerman\Events::onMessage is not callable

5年前 评论

解决了,events引用错了

5年前 评论

集成进来以后能在 workerman 的各种事件里适用 laravel ORM 吗? 貌似不行.

5年前 评论

@overfalse

    public static function onWebSocketConnect($client_id, $data)
    {
        echo "onWebSocketConnect\r\n";
        Auth::guard('api')->setToken($data['server']['HTTP_AUTHORIZATION']);
        Gateway::bindUid($client_id, Auth::guard('api')->id());
        Gateway::joinGroup($client_id, $data['server']['HTTP_ROOMID']);
    }
5年前 评论

@小陈叔叔 我集成 Workerman , 然后在 Workerman 的回调里面适用 orm, 前几分钟可以, 再久一点就不行了, 应该是生命周期问题. 不知道你这里有没解决方案.

5年前 评论
zero风来 4年前
overfalse (作者) 4年前
zero风来 4年前

@overfalse GatewayWorker最好不处理任何业务逻辑,如果你需要服务端向客户端推送数据的话可以用Gateway的API(GatewayClient),这样你就可以在controller调用api就行。

5年前 评论
zero风来 4年前

@overfalse 这个还真没试过,只是集成了有些东西还没来的急去重构,如果数据库断开的话,你可以看一下数据库的重连机制,或者是使用长连接

5年前 评论
ThinkQ

很不错!

5年前 评论

不错的整合文章,谢谢分享。

5年前 评论

@小陈叔叔 你是不是少了一条用php artisan创建文件的命令了啊?

5年前 评论

@overfalse 你需要断线重连机制。

5年前 评论
巴啦啦

做完啊,stop,reload, -d

5年前 评论

@HectorChan 自动创建和手动创建的结果都是一样的 主要是这个思路

5年前 评论

如果一个项目有多个 TCP 的情况下如何处理呢?
比如:2346 端口用于 Websocket ,2347 端口用于 TCP 的情况。

5年前 评论

陈叔 现在我们部署到线上之后 使用了supervisor来进行管理 然后代码里面是4个进程 但是过一段时间后出来了很多进程 您遇到过吗

5年前 评论

@Echor supervisor默认的配置,会把PID文件 存在tmp文件夹下面,tmp偶尔会被清空

https://www.cjango.com/d/38.html 参考一下我这篇看看,不知道能不能解决你的问题

5年前 评论

@小陈叔叔 行 谢谢陈叔 我先看看 谢谢您!

5年前 评论

请问具体用法有说明文档么 ,没用过socket 想学习下

5年前 评论
kingThegirl 4年前
UKNOW

file我执行这个命令报错

5年前 评论
woshily 3年前
水皮 4年前

请问在 Events 中有办法验证 jwt 用户吗?

5年前 评论

@overfalse 参考这个http://doc2.workerman.net/326107 然后通过http请求去调用workerman 服务器做 workerman服务器只做专门的消息推送服务器,这样的话就可以了

5年前 评论

@小陈叔叔 pid文件有时候会被清理,您遇见过吗?pid清理后只影响stop和status命令,有什么危害吗?

5年前 评论

用的-d,pid是存在vendor扩展包下的

5年前 评论

我知道了,ci的时候被清了 :joy:

5年前 评论

感谢分享。

5年前 评论

我要用--d才能start

5年前 评论
恶龙咆哮,啊呜!

发消息收不到啊

4年前 评论

@Echor 这个问题你解决了吗?我现在也遇到了 T.T

4年前 评论

一定要用Gateway吗

4年前 评论

win10怎么启动么

4年前 评论
linzening 4年前
假如_丶 (作者) 4年前
linzening 4年前

Command "workerman" is not defined.

4年前 评论
ggz1011 2年前

file
如果windows遇到输入命令后不显示进程也不报错。

问题原因:
Windows无法进行同时启动多个协议
由于PHP-CLI在windows系统无法实现多进程以及守护进程,所以windows版本Workerman建议仅作开发调试使用。

4年前 评论
小陈叔叔 (楼主) 4年前
sunrise丶 4年前
Hachiko 4年前
Hachiko 4年前
sunrise丶 4年前
Hachiko 4年前
思远 (作者) 4年前
sunrise丶 4年前

启动命令应该是:

php artisan workman start --d
4年前 评论
kingThegirl 4年前

针对 Windows 下无法启动的问题,参考:windows操作系统下如何初始化多个Worker,做了一点特殊处理,也可以自己改进

1、命令行文件,改动

    protected $signature = 'workerman
                            {action : action}
                            {--start=all : start}
                            {--d : daemon mode}';

    public function handle() {
        global $argv;
        $action = $this->argument('action');
        /**
         * 针对 Windows 一次执行,无法注册多个协议的特殊处理
         */
        if ($action === 'single') {
            $start = $this->option('start');
            if ($start === 'register') {
                $this->startRegister();
            } elseif ($start === 'gateway') {
                $this->startGateWay();
            } elseif ($start === 'worker') {
                $this->startBusinessWorker();
            }
            Worker::runAll();

            return;
        }

        /**
         * argv[0] 默认是,当前文件,可以不修改
         */
        //$argv[0] = 'wk';
        $argv[1] = $action;
        // 控制是否进入 daemon 模式
        $argv[2] = $this->option('d') ? '-d' : '';

        $this->start();
    }

2、项目根目录,新建 start_for_win.bat

start /b php artisan workerman single --start=register
start /b php artisan workerman single --start=gateway
start /b php artisan workerman single --start=worker
pause

执行效果截图:

file

注:生产环境还是用 Linux 吧

4年前 评论
Wall_E 2年前
思远 4年前
bing 3年前

[@Hachiko](https://learnku.com/users/22249) 我用workerman而已。没用gateway,所以我只开一个worker

      /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
            global $argv;
            $action = $this->argument('action');

            $argv[0] = 'wk';
            $argv[1] = $action;

            $this->startWorker();
    }

    protected function startWorker()
    {
        $worker = new Worker("protocol://0.0.0.0:4001");
        $worker->count = 1;

        $worker->onMessage = function ($connection, $data) {

        };
        $worker->onWorkerStart = function ($connection) {

        };
        Worker::runAll();
    }

一样是卡住。出不来

4年前 评论
Hachiko 4年前
sunrise丶 (作者) 4年前
sunrise丶 (作者) 4年前

请教一下大佬,我的定制协议文件应该放在哪呢

4年前 评论

说好的socket 怎么成websocket了 :sob:

3年前 评论

事件类中的方法会自动触发么?我没看到任何关联

3年前 评论

在windows下可以使用吗,我的websocket连不上

3年前 评论

如何使用laravel的功能测试来测试workman

3年前 评论

Artisan::call('socket:connect stop');

我在start加了进程守护,然后想通过程序重启socket, 结果提示 Only run in command line mode

请问如何重启socket,通过程序

2年前 评论

我想问一下,1236 , 2346 , 2300 这三个端口都是做什么的?哪些需要外网开放啊?

2年前 评论
yzbfeng 1年前
Complicated

我想请教下,怎么配置wss,就是 server.pem,server.key 文件路径

5个月前 评论
Hachiko 5个月前
Complicated (作者) 5个月前
Complicated

配置wss的方法

file

5个月前 评论
Complicated

正确的启动命令是 php artisan gateway-worker:server start –daemon(两个横杠)

5个月前 评论

我有写一个包,觉得不错的可以用用 github.com/aoeng/laravel-workerman

5个月前 评论
Complicated 5个月前
UpGod (作者) 5个月前
Complicated

记录一个问题。我这边的场景:当收到消息后,在队列里塞一个1分钟执行的任务,任务的内容是广播给当前小组的人一条信息。然后队列任务执行失败了!报错“ErrorException: stream_socket_client(): Unable to connect to tcp://127.0.0.1:1236 ”,因为我把端口号改成 1238了。普通法消息都没问题,但是为啥队列里的代码去用1236呢?找了下源码,在vendor\workerman\gateway-worker\src\Lib\Gateway.php 里有个静态的 $registerAddress = 127.0.0.1:1236。肯定就是它了!因为队列也是常驻内存,所以这种静态的只能改源码了!

5个月前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!