GatewayWorker 与 Workerman的关系
Workerman可以看做是一个纯粹的socket类库,可以开发几乎所有的网络应用,不管是TCP的还是UDP的,长连接的还是短连接的。Workerman代码精简,功能强大,使用灵活,能够快速开发出各种网络应用。同时Workerman相比GatewayWorker也更底层,需要开发者有一定的多进程编程经验。
因为绝大多数开发者的目标是基于Workerman开发TCP长连接应用,而长连接应用服务端有很多共同之处,例如它们有相同的进程模型以及单发、群发、广播等接口需求。所以才有了GatewayWorker框架,GatewayWorker是基于Workerman开发的一个TCP长连接框架,实现了单发、群送、广播等长连接必用的接口。GatewayWorker框架实现了Gateway Worker进程模型,天然支持分布式多服务器部署,扩容缩容非常方便,能够应对海量并发连接。可以说GatewayWorker是基于Workerman实现的一个更完善的专门用于实现TCP长连接的项目框架。
用GatewayWorker还是Workerman?
如果你的项目是长连接并且需要客户端与客户端之间通讯,建议使用GatewayWorker。 短连接或者不需要客户端与客户端之间通讯的项目建议使用Workerman。 GatewayWorker不支持UDP监听,所以UDP服务请选择Workerman。 如果你是一个有多进程socket编程经验的人,喜欢定制自己的进程模型,可以选择Workerman。
一、在 composer.json 文件的 require 引入包
"workerman/gateway-worker": "^3.0",
"workerman/gatewayclient": "^3.0",
"workerman/workerman": "^3.5"
终端的项目根目录输入 composer install,也可以composer update不过不建议
二、在 App\Console\Commands 目录创建 GatewayWorkerServer.php 文件,作为服务端口的启动文件
<?php
namespace App\Console;
use App\GatewayWorker\Events;
use GatewayWorker\BusinessWorker;
use GatewayWorker\Gateway;
use GatewayWorker\Register;
use Illuminate\Console\Command;
use Workerman\Worker;
class GatewayWorkerServer extends Command
{
protected $signature = "gateway-worker:server {action} {--daemon}";
protected $description = "Start a GatewayWorker Server.";
public function __construct()
{
parent::__construct();
}
public function handle()
{
global $argv;
if (!in_array($action = $this->argument('action'), ['start', 'stop', 'restart'])) {
$this->error('Error Arguments');
exit;
}
$argv[0] = 'gateway-worker:server';
$argv[1] = $action;
$argv[2] = $this->option('daemon') ? '-d' : '';
$this->start();
}
public function start()
{
$this->startGateWay();
$this->startBusinessWorker();
$this->startRegister();
Worker::runAll();
}
public function startBusinessWorker()
{
$work = new BusinessWorker();
$work->name = 'BusinessWorker';
$work->count = 2;
$work->registerAddress = '127.0.0.1:9501';
$work->eventHandler = Events::class;
}
public function startGateWay()
{
$gateway = new Gateway("websocket://0.0.0.0:9502");
$gateway->name = 'Gateway';
$gateway->count = 2;
$gateway->lanIp = '127.0.0.1';
$gateway->startPort = 9000;
$gateway->pingInterval = 30;
$gateway->pingNotResponseLimit = 0;
$gateway->pingData = '{"mode":"heart"}';
$gateway->registerAddress = '127.0.0.1:9501';
}
public function startRegister()
{
new Register('text://127.0.0.1:9501');
}
}
注意事项: (1)$gateway->startPort监听本机端口的起始端口要小与其它端口 (2)进程数为CPU核数的1-2倍左右即可,当然如果你是32核的你也可以设置进程数为8,看实际情况而定
三、在 App\Console\Kernel 引入 GatewayWorkerServer::class
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
protected $commands = [
GatewayWorkerServer::class
];
protected function schedule(Schedule $schedule)
{
}
}
也可加上面的 protected function commands ,然后在routes/consele.php里加以下代码
Artisan::command('inspire', function () {
$this->comment(Inspiring::quote());
})->describe('Display an inspiring quote');
四、在 app 目录下创建 GatewayWorker/Events.php 监听文件
<?php
namespace App\GatewayWorker;
use App\Models\Order;
use App\Models\Chat;
use GatewayWorker\Lib\Gateway;
use Illuminate\Support\Facades\Log;
class Events
{
public static function onWorkerStart($businessWorker)
{
echo "BusinessWorker Start\n";
}
public static function onConnect($client_id)
{
Gateway::sendToClient($client_id, json_encode(['type' => 'init', 'client_id' => $client_id]));
}
public static function onWebSocketConnect($client_id, $data)
{
}
public static function onMessage($client_id, $message)
{
$response = ['status' => 200, 'msg' => 'ok', 'data' => $message];
$message = json_decode($message);
if (!isset($message->mode)) {
$response['msg'] = 'missing parameter mode';
$response['errcode'] = ERROR_CHAT;
Gateway::sendToClient($client_id, json_encode($response));
return false;
}
$from_uid = $message->from_uid;
$to_uid = $message->to_uid;;
switch ($message->mode) {
case 'say':
$chats = Chats::create(['from_id' => $from_uid, 'to_id' => $to_uid, 'content' => $content, 'no' => $count + 1]);
$chats = Chats::select(['from_id', 'to_id', 'content'])->find($chats->id);
$response['data'] = $chats;
if (Gateway::isUidOnline($to_uid)) {
$response['msg'] = '已发送至uid为:' . $to_uid;
Gateway::sendToUid($to_uid, json_encode($response));
} else {
$response['msg'] = '对方不在线';
}
break;
case 'chats':
$chats = Chat::where('from_id', $message->from_id)->get();
$response['data'] = ['chats' => $chats];
break;
default:
$response['status'] = 201;
$response['msg'] = 'Undefined';
}
Gateway::sendToClient($client_id, json_encode($response));
}
public static function onClose($client_id)
{
Log::info('close connection' . $client_id);
}
}
注意:每次修改events.php后,记得重启,记得重启,记得重启
五、开启服务终端,命令行输入
php artisan gateway-worker:server start 其它命令:
以debug(调试)方式启动:php artisan gateway-worker:server start
以daemon(守护进程)方式启动:php artisan gateway-worker:server start --daemon
停止:php artisan gateway-worker:server stop
重启:php artisan gateway-worker:server restart
平滑重启:php artisan gateway-worker:server reload
查看状态:php artisan gateway-worker:server status .
测试:在浏览器 F12 打开调试模式,在 Console 里输入
ws = new WebSocket("ws://127.0.0.1:9502");
ws.onopen = function() {
ws . send('{"mode":"say","from_uid":1,to_uid:2,"content":"文字内容"}');
ws . send('{"mode":"chats","from_uid":1}');
};
ws.onmessage = function(e) {
console.log("收到服务端的消息:" + e.data);
};
就能打印出对应的信息了
参考: Workerman官方手册、GatewayWorker官方手册、Laravel官方手册、 https://learnku.com/articles/25208
|