由于项目需要,要在laravel上面使用workerman,在网上查了好多资料,总结下,做下笔记,以便以后需要
一、安装 workerman
在项目根目录执行
composer require workerman/workerman
二、创建自定义 artisan 命令来启动 workerman 服务
由于 laravel 不能直接在根目录下执行 php
命令,所以需要创建 artisan
命令用于后面 workerman 服务的开启。
1,生成 WorkermanCommand 文件
php artisan make:command WorkermanCommand
执行以上命令行会在 app/Console/Commands/ 目录下生成 文件。
<?php namespace App\Console\Commands; use Workerman\Worker; use Illuminate\Console\Command; class WorkermanCommand extends Command { private $server; // -d 是否以debug方式运行 protected $signature = 'workerman {action} {-d?}'; protected $description = 'Start a Workerman server.'; public function __construct() { parent::__construct(); } /** * Execute the console command. * * @return mixed */ public function handle() { global $argv; $arg = $this->argument('action'); $argv[1] = $argv[2]; $argv[2] = isset($argv[3]) ? "-{$argv[3]}" : ''; switch ($arg) { case 'start': $this->start(); break; case 'stop': $this->stop(); break; case 'restart': $this->restart(); break; case 'reload': $this->reload(); break; } } private function start() { // 创建一个Worker监听20002端口,不使用任何应用层协议 $this->server = new Worker("tcp://0.0.0.0:8383"); // 启动4个进程对外提供服务 $this->server->count = 4; $handler = \App::make('handlers\WorkermanHandler'); // 连接时回调 $this->server->onConnect = [$handler, 'onConnect']; // 收到客户端信息时回调 $this->server->onMessage = [$handler, 'onMessage']; // 进程启动后的回调 $this->server->onWorkerStart = [$handler, 'onWorkerStart']; // 断开时触发的回调 $this->server->onClose = [$handler, 'onClose']; // 运行worker Worker::runAll(); } private function stop() { $worker = new Worker('tcp://0.0.0.0:8383'); // 设置此实例收到reload信号后是否reload重启 $worker->reloadable = false; $worker->onWorkerStop = function($worker) { echo "Worker reload...\n"; }; // 运行worker Worker::runAll(); } private function restart() { $worker = new Worker('tcp://0.0.0.0:8383'); // 设置此实例收到reload信号后是否reload重启 $worker->reloadable = true; $worker->onWorkerStart = function($worker) { echo "Worker restart...\n"; }; // 运行worker Worker::runAll(); } private function reload() { $worker = new Worker('tcp://0.0.0.0:8383'); // 设置此实例收到reload信号后是否reload重启 $worker->reloadable = false; $worker->onWorkerStart = function($worker) { echo "Worker reload...\n"; }; // 运行worker Worker::runAll(); } }
这里使用了 PHP 类方法的回调。(PHP几种回调写法)
这里我们创建了一个自定义命令 workerman [action] [-d]
,通过此命令即可开启 workerman 服务。
在这个自定义命令还引用了其他的类文件,如:
$handler = \App::make('handlers\WorkermanHandler');
所以,需要创建一个 的文件来处理对应的操作。
还需要在app/Console/Commands/ 中增加
protected $commands = [ // \App\Console\Commands\WorkermanCommand::class ];
2,创建
创建文件 app/handlers/
<?php namespace handlers; use Workerman\Lib\Timer; // 心跳间隔10秒 define('HEARTBEAT_TIME',50); class WorkermanHandler { // 处理客户端连接 public function onConnect($connection) { echo "链接成功"; } // 处理客户端消息 public function onMessage($connection, $data) { //当上来数据获取最后上传数据的时间 $connection->lastMessageTime = time(); // 向客户端发送hello $data $connection->send('Hello, your send message is: ' . $data); } // 处理客户端断开 public function onClose($connection) { echo "connection closed from ip {$connection->getRemoteIp()}\n"; } public function onWorkerStart($worker) { Timer::add(1, function () use ($worker) { $time_now = time(); foreach ($worker->connections as $connection) { // 有可能该connection还没收到过消息,则lastMessageTime设置为当前时间 if (empty($connection->lastMessageTime)) { $connection->lastMessageTime = $time_now; continue; } // 上次通讯时间间隔大于心跳间隔,则认为客户端已经下线,关闭连接 if ($time_now - $connection->lastMessageTime > HEARTBEAT_TIME) { echo "Client ip {$connection->getRemoteIp()} timeout!!!\n"; $connection->close(); } } }); } }
3,修改 文件,让
app/handles
文件夹下的类文件自动加载。
"autoload": {
"classmap": [
...
"app/handles"
],
...
},
至此。workman的命令定义已经完成。
使用:
php artisan workerman start d
如果看到以下内容,说明 workerman 服务启动正常:
Workerman[artisan] start in DEBUG mode
----------------------- WORKERMAN -----------------------------
Workerman version:3.5.4 PHP version:7.1.4
------------------------ WORKERS -------------------------------
user worker listen processes status
root none tcp://0.0.0.0:8383 1 [OK]
----------------------------------------------------------------
Start success.