安装
composer require workerman/gateway-worker
composer require workerman/gatewayclient
创建 Workerman 启动文件app/Console/Commands/WorkermanCommand
命令:php artisan make:command WorkermanCommand
<?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->startPort = 2300;
$gateway->pingInterval = 30;
$gateway->pingNotResponseLimit = 0;
$gateway->registerAddress = '127.0.0.1:1236';
}
private function startRegister()
{
new Register('text://0.0.0.0:1236');
}
}
创建事件监听文件app/Workerman/Events.php
<?php
namespace App\Workerman;
use GatewayClient\Gateway;
use Workerman\Lib\Timer;
class Events
{
public static function onWorkerStart($businessWorker)
{
}
public static function onConnect($client_id)
{
Gateway::sendToClient($client_id, json_encode(array(
'type' => 'init',
'client_id' => $client_id
)));
//定时检测mac地址
Timer::add(1800, function () use ($client_id) {
$data = array('type' => 'mac');
Gateway::sendToClient($client_id, json_encode($data));
});
}
public static function onWebSocketConnect($client_id, $data)
{
}
public static function onMessage($client_id, $message)
{
}
public static function onClose($client_id)
{
}
}
启动 Workerman 服务端
php artisan workman start --d
后台使用
<?php
namespace app\Http\Controllers;
use app\Models\User;
use GatewayClient\Gateway;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Cache;
class WorkermanController extends Controller
{
public function __construct()
{
Gateway::$registerAddress = '127.0.0.1:1236';
}
/**
* websocket连接的client_id绑定用户的id
* @param Request $request
* @return JsonResponse
*/
public function bind(Request $request): JsonResponse
{
try {
$uid = auth()->id();
$roles = Cache::get('user_role_' . $uid);
Gateway::bindUid($request->get('client_id'), $uid);
foreach ($roles as $role) {
Gateway::joinGroup($request->get('client_id'), 'role' . $role);
$menus = Cache::get('role_menu_' . $role);
foreach ($menus as $menu) {
Gateway::joinGroup($request->get('client_id'), 'menu' . $menu->id);
}
}
$data = array('msg' => '绑定成功');
return res(compact('data'));
} catch (\Exception $e) {
throw new \Exception('绑定失败');
}
}
/**
* 根据用户更新权限
* @param $userId
*/
public function update_menus($userId)
{
$data = [];
$permissions = User::menus($userId);
$user = new User();
$data['menus'] = recursion($permissions['menus'], 'id', 'parent_id', 'children', 0, false);
$data['permissions'] = $permissions['webPermissions'];
$user->update_token($userId, $permissions['permissions']);
Gateway::sendToUid($userId, json_encode(array('type' => 'menus', 'data' => $data)));
}
public function update_menu_user($userId)
{
$userIds = Gateway::getAllUidList();
if (in_array($userId, $userIds)) {
$this->update_menus($userId);
}
}
/**
* 如果修改了角色数据,则用户更新权限
* @param $roleId
*/
public function update_menu_role($roleId)
{
$userIds = Gateway::getUidListByGroup('role' . $roleId);
foreach ($userIds as $userId) {
$this->update_menus($userId);
}
}
/**
* 如果修改了菜单数据,则用户更新权限
* @param $menuId
*/
public function update_menu_menu($menuId)
{
$userIds = Gateway::getUidListByGroup('menu' . $menuId);
foreach ($userIds as $userId) {
$this->update_menus($userId);
}
}
/**
* 更新登陆状态
* @param $userId
*/
public function update_login($userId)
{
$userIds = Gateway::getAllUidList();
if (in_array($userId, $userIds)) {
$data = User::query()->find($userId);
$data->tokens()->where('tokenable_id', '=', $userId)->delete();
Gateway::sendToUid($userId, json_encode(array('type' => 'logout')));
}
}
}
前端使用
ws = new WebSocket('ws://192.168.10.106:2346');
// 连接成功发送用户信息给后端
ws.onopen = function () {
};
// 收到消息保存到vuex
ws.onmessage = async function (e) {
const type = JSON.parse(e.data).type
const client_id = JSON.parse(e.data).client_id
if (type === 'init') {
//用户绑定client_id
} else if (type === 'menus') {
//更新菜单
} else if (type === 'logout') {
//退出
} else if (type === 'mac') {
//检测mac地址
}
}
注意
部署线上环境的时候,如果使用docker容易,需要将使用的端口放开。