This commit is contained in:
Fabian @ Blax Software 2026-03-14 09:32:24 +01:00
parent 636d9eccb8
commit a2a5524637
3 changed files with 143 additions and 0 deletions

View File

@ -74,6 +74,13 @@ class StartServer extends Command
*/
protected $restartSoftShutdown = false;
/**
* The last steer signal timestamp.
*
* @var int|null
*/
protected $lastSteer = null;
/**
* Initialize the command.
*
@ -148,6 +155,10 @@ class StartServer extends Command
$this->configureRestartTimer();
\Log::channel('websocket')->debug('Restart timer configured');
\Log::channel('websocket')->debug('Configuring steer timer...');
$this->configureSteerTimer();
\Log::channel('websocket')->debug('Steer timer configured');
\Log::channel('websocket')->debug('Configuring routes...');
$this->configureRoutes();
\Log::channel('websocket')->debug('Routes configured');
@ -290,6 +301,84 @@ class StartServer extends Command
\Log::channel('websocket')->debug('WebSocket routes registered');
}
/**
* Configure the timer that polls for steer signals (cache:clear, etc.).
*
* @return void
*/
public function configureSteerTimer(): void
{
$steerData = Cache::store('file')->get('blax:websockets:steer');
$this->lastSteer = $steerData['time'] ?? null;
\Log::channel('websocket')->debug('Steer timer configured', [
'initial_steer_time' => $this->lastSteer,
]);
$this->loop->addPeriodicTimer(5, function () {
$steerData = Cache::store('file')->get('blax:websockets:steer');
$currentSteer = $steerData['time'] ?? null;
if ($currentSteer !== null && $currentSteer !== $this->lastSteer) {
$action = $steerData['action'] ?? null;
$this->lastSteer = $currentSteer;
\Log::channel('websocket')->info("Steer signal received: {$action}");
$this->handleSteerAction($action);
}
});
}
/**
* Execute a steer action received via the cache signal.
*/
protected function handleSteerAction(?string $action): void
{
switch ($action) {
case 'cache:clear':
$this->steerCacheClear();
break;
case 'restart':
$this->restartSoftShutdown = false;
$this->triggerShutdown(true);
break;
case 'restart:soft':
$this->restartSoftShutdown = true;
$this->triggerShutdown(true);
break;
default:
\Log::channel('websocket')->warning("Unknown steer action: {$action}");
}
}
/**
* Clear OPcache and the controller resolution cache so the running
* server picks up new code from disk without a full restart.
*/
protected function steerCacheClear(): void
{
$cleared = [];
// 1. Reset OPcache — forces PHP to recompile files from disk
if (function_exists('opcache_reset')) {
opcache_reset();
$cleared[] = 'opcache';
}
// 2. Clear controller resolver cache so new/changed controllers are discovered
if (class_exists(\BlaxSoftware\LaravelWebSockets\Websocket\ControllerResolver::class)) {
\BlaxSoftware\LaravelWebSockets\Websocket\ControllerResolver::clearCache();
\BlaxSoftware\LaravelWebSockets\Websocket\ControllerResolver::preload();
$cleared[] = 'controllers';
}
\Log::channel('websocket')->info('Steer cache:clear executed', ['cleared' => $cleared]);
$this->line('Cache cleared: ' . implode(', ', $cleared));
}
/**
* Configure the PCNTL signals for soft shutdown.
*

View File

@ -0,0 +1,53 @@
<?php
namespace BlaxSoftware\LaravelWebSockets\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\InteractsWithTime;
class SteerServer extends Command
{
use InteractsWithTime;
protected $signature = 'websocket:steer
{action : The action to send (cache:clear, restart, restart:soft)}
{--cache-driver=file : The cache driver to use for signaling.}';
protected $description = 'Send a steering command to the running WebSocket server.';
/** @var array<string, string> */
protected array $actions = [
'cache:clear' => 'Clear OPcache and controller resolution cache (picks up new code without full restart)',
'restart' => 'Hard-restart the server (stops loop, supervisord restarts the process)',
'restart:soft' => 'Soft-restart the server (gracefully close connections first)',
];
public function handle(): int
{
$action = $this->argument('action');
if (! array_key_exists($action, $this->actions)) {
$this->error("Unknown action: {$action}");
$this->line('');
$this->info('Available actions:');
foreach ($this->actions as $name => $desc) {
$this->line(" <comment>{$name}</comment> {$desc}");
}
return self::FAILURE;
}
$store = $this->option('cache-driver') ?: 'file';
Cache::store($store)->forever('blax:websockets:steer', [
'action' => $action,
'time' => $this->currentTime(),
]);
\Log::channel('websocket')->info("WebSocket steer signal sent: {$action}");
$this->info("Sent '{$action}' signal to the WebSocket server.");
$this->line("<comment>{$this->actions[$action]}</comment>");
return self::SUCCESS;
}
}

View File

@ -202,6 +202,7 @@ class WebSocketsServiceProvider extends ServiceProvider
$this->commands([
Console\Commands\StartServer::class,
Console\Commands\RestartServer::class,
Console\Commands\SteerServer::class,
Console\Commands\CleanStatistics::class,
Console\Commands\FlushCollectedStatistics::class,
]);