没有听说过或者没有用过 workerman 的童鞋建议先去官网看下文档。
一、安装 workerman
在项目根目录执行
composer require workerman/workerman
二、创建自定义 artisan 命令来启动 workerman 服务
由于 laravel 不能直接在根目录下执行 php
命令,所以需要创建 artisan
命令用于后面 workerman 服务的开启。
1,生成 WorkermanCommand 文件
php artisan make:command WorkermanCommand
执行以上命令行会在 app/Console/Commands/ 目录下生成 WorkermanCommand.php 文件。
<?php
namespace App\Console\Commands;
use Workerman\Worker;
use Illuminate\Console\Command;
class WorkermanCommand extends Command
{
private $server;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'wk {action}';
/**
* 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;
$arg = $this->argument('action');
$argv[1] = $argv[2];
$argv[2] = isset($argv[3]) ? "-{$argv[3]}" : '';
switch ($arg) {
case 'start':
$this->start();
break;
case 'stop':
break;
case 'restart':
break;
case 'reload':
break;
case 'status':
break;
case 'connections':
break;
}
}
private function start()
{
// 创建一个Worker监听20002端口,不使用任何应用层协议
$this->server = new Worker("tcp://0.0.0.0:20002");
// 启动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();
}
}
我只实现了 start
命令,其他命令童鞋们自行实现吧。
这里使用了 PHP 类方法的回调。(PHP几种回调写法)
这里我们创建了一个自定义命令 wk [action]
,通过此命令即可开启 workerman 服务。
在这个自定义命令还引用了其他的类文件,如:
$handler = \App::make('handlers\WorkermanHandler');
所以,需要创建一个 WorkermanHandler.php
的文件来处理对应的操作。
2,创建 WorkermanHandler.php
创建文件 app/handlers/WorkermanHandler.php
<?php
namespace handlers;
use Workerman\Lib\Timer;
// 心跳间隔10秒
define('HEARTBEAT_TIME', 10);
class WorkermanHandler
{
// 处理客户端连接
public function onConnect($connection)
{
echo "new connection from ip " . $connection->getRemoteIp() . "\n";
}
// 处理客户端消息
public function onMessage($connection, $data)
{
// 向客户端发送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,修改 composer.json
文件,让 app/Protocols
文件夹下的类文件自动加载。
"autoload": {
"classmap": [
...
"app/Protocols"
],
...
},
至此。workman的命令定义已经完成。
使用:
php artisan wk start
如果看到以下内容,说明 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:20002 1 [OK]
----------------------------------------------------------------
Press Ctrl+C to quit. Start success.
帮助文档:
心跳包
如何与其它框架整合
利用WorkerMan给客户端推送数据