在 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 协议》,转载必须注明作者和本文链接
本帖由系统于 1年前 自动加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 37

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

1年前 评论

解决了,events引用错了

1年前 评论

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

1年前 评论

@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']);
    }
1年前 评论

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

1年前 评论
zero风来 1个月前
overfalse (作者) 1个月前
zero风来 1个月前

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

1年前 评论
zero风来 1个月前

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

1年前 评论
ThinkCsly

很不错!

1年前 评论

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

1年前 评论

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

1年前 评论
Linxb

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

1年前 评论
巴啦啦

做完啊,stop,reload, -d

1年前 评论

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

1年前 评论

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

1年前 评论

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

1年前 评论

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

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

1年前 评论

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

1年前 评论

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

1年前 评论
kingThegirl 4个月前
UKNOW

file我执行这个命令报错

1年前 评论
水皮 3个月前

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

1年前 评论

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

1年前 评论

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

1年前 评论

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

1年前 评论

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

1年前 评论

感谢分享。

1年前 评论

我要用--d才能start

11个月前 评论

发消息收不到啊

10个月前 评论

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

10个月前 评论

一定要用Gateway吗

9个月前 评论
Zippo

win10怎么启动么

9个月前 评论
linzening 9个月前
Zippo (作者) 8个月前
linzening 8个月前

Command "workerman" is not defined.

7个月前 评论

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

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

7个月前 评论
小陈叔叔 (楼主) 7个月前
sunrise丶 5个月前
Hachiko 5个月前
Hachiko 5个月前
sunrise丶 5个月前
Hachiko 5个月前
siYuanJun (作者) 5个月前
sunrise丶 5个月前

启动命令应该是:

php artisan workman start --d
5个月前 评论
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 吧

5个月前 评论
siYuanJun 5个月前

[@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();
    }

一样是卡住。出不来

5个月前 评论
Hachiko 5个月前
sunrise丶 (作者) 5个月前
sunrise丶 (作者) 5个月前

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

1周前 评论

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!