I restart logic

This commit is contained in:
Fabian @ Blax Software 2026-01-20 18:11:22 +01:00
parent ddbca89cf7
commit 79afcdeb93
2 changed files with 91 additions and 24 deletions

View File

@ -16,7 +16,8 @@ class RestartServer extends Command
* @var string * @var string
*/ */
protected $signature = 'websockets:restart protected $signature = 'websockets:restart
{--cache-driver=file : The cache driver to use for the server. Redis will not work due to concurrency issues.}'; {--cache-driver=file : The cache driver to use for the server. Redis will not work due to concurrency issues.}
{--soft : Use soft shutdown (gracefully close connections) instead of hard shutdown.}';
/** /**
* The console command description. * The console command description.
@ -38,11 +39,15 @@ class RestartServer extends Command
Cache::forever( Cache::forever(
'blax:websockets:restart', 'blax:websockets:restart',
$this->currentTime() [
'time' => $this->currentTime(),
'soft' => $this->option('soft'),
]
); );
$shutdownType = $this->option('soft') ? 'soft' : 'hard';
$this->info( $this->info(
'Broadcasted the restart signal to the WebSocket server!' "Broadcasted the {$shutdownType} restart signal to the WebSocket server!"
); );
} }
} }

View File

@ -33,6 +33,7 @@ class StartServer extends Command
{--statistics-interval= : The amount of seconds to tick between statistics saving.} {--statistics-interval= : The amount of seconds to tick between statistics saving.}
{--debug : Forces the loggers to be enabled and thereby overriding the APP_DEBUG setting.} {--debug : Forces the loggers to be enabled and thereby overriding the APP_DEBUG setting.}
{--loop : Programatically inject the loop.} {--loop : Programatically inject the loop.}
{--soft : Use soft shutdown (gracefully close connections) instead of hard shutdown.}
'; ';
/** /**
@ -56,6 +57,20 @@ class StartServer extends Command
*/ */
public $server; public $server;
/**
* The last restart timestamp.
*
* @var int|null
*/
protected $lastRestart = null;
/**
* Whether the last restart signal requested soft shutdown.
*
* @var bool
*/
protected $restartSoftShutdown = false;
/** /**
* Initialize the command. * Initialize the command.
* *
@ -182,18 +197,27 @@ class StartServer extends Command
*/ */
public function configureRestartTimer() public function configureRestartTimer()
{ {
$this->lastRestart = $this->getLastRestart(); $restartData = $this->getLastRestartData();
$this->lastRestart = $restartData['time'] ?? null;
$this->loop->addPeriodicTimer(10, function () { $this->loop->addPeriodicTimer(10, function () {
if ( $restartData = $this->getLastRestartData();
($this->getLastRestart() . '') $currentRestart = $restartData['time'] ?? null;
!== ($this->lastRestart . '')
) { // Only trigger restart if lastRestart was set and a new restart signal was received
\Log::channel('websocket')->info('Restart detected, triggering soft shutdown...', [ if ($this->lastRestart !== null && $currentRestart !== null && $currentRestart !== $this->lastRestart) {
$this->restartSoftShutdown = $restartData['soft'] ?? false;
\Log::channel('websocket')->info('Restart detected, triggering shutdown...', [
'previous_restart' => $this->lastRestart, 'previous_restart' => $this->lastRestart,
'current_restart' => $this->getLastRestart(), 'current_restart' => $currentRestart,
'soft' => $this->restartSoftShutdown,
]); ]);
$this->triggerSoftShutdown();
// Update lastRestart to prevent multiple triggers
$this->lastRestart = $currentRestart;
$this->triggerShutdown(true);
} }
}); });
} }
@ -225,17 +249,17 @@ class StartServer extends Command
} }
$this->loop->addSignal(SIGTERM, function () { $this->loop->addSignal(SIGTERM, function () {
\Log::channel('websocket')->info('Received SIGTERM, closing existing connections...'); \Log::channel('websocket')->info('Received SIGTERM, shutting down...');
$this->line('Closing existing connections...'); $this->line('Shutting down server...');
$this->triggerSoftShutdown(); $this->triggerShutdown();
}); });
$this->loop->addSignal(SIGINT, function () { $this->loop->addSignal(SIGINT, function () {
\Log::channel('websocket')->info('Received SIGINT, closing existing connections...'); \Log::channel('websocket')->info('Received SIGINT, shutting down...');
$this->line('Closing existing connections...'); $this->line('Shutting down server...');
$this->triggerSoftShutdown(); $this->triggerShutdown();
}); });
} }
@ -341,26 +365,64 @@ class StartServer extends Command
} }
/** /**
* Get the last time the server restarted. * Get the last restart data from cache.
* *
* @return int * @return array
*/ */
protected function getLastRestart() protected function getLastRestartData()
{ {
return Cache::get( $data = Cache::get('blax:websockets:restart');
'blax:websockets:restart',
0 // Handle legacy format (just timestamp) for backwards compatibility
); if (is_numeric($data)) {
return ['time' => $data, 'soft' => false];
}
return $data ?? ['time' => null, 'soft' => false];
}
/**
* Trigger shutdown based on the --soft option or restart signal.
*
* @return void
*/
protected function triggerShutdown(bool $fromRestart = false)
{
// Check restart signal's soft flag first, then fall back to command option
$useSoftShutdown = $fromRestart ? $this->restartSoftShutdown : $this->option('soft');
if ($useSoftShutdown) {
$this->triggerSoftShutdown();
} else {
$this->triggerHardShutdown();
}
}
/**
* Trigger a hard shutdown for the process.
* Immediately stops the loop without gracefully closing connections.
*
* @return void
*/
protected function triggerHardShutdown()
{
\Log::channel('websocket')->info('Triggering hard shutdown...');
$this->line('Hard shutdown initiated, stopping server immediately...');
$this->loop->stop();
} }
/** /**
* Trigger a soft shutdown for the process. * Trigger a soft shutdown for the process.
* Gracefully closes all connections before stopping.
* *
* @return void * @return void
*/ */
protected function triggerSoftShutdown() protected function triggerSoftShutdown()
{ {
\Log::channel('websocket')->info('Triggering soft shutdown...'); \Log::channel('websocket')->info('Triggering soft shutdown...');
$this->line('Soft shutdown initiated, closing existing connections gracefully...');
$channelManager = $this->laravel->make(ChannelManager::class); $channelManager = $this->laravel->make(ChannelManager::class);
// Close the new connections allowance on this server. // Close the new connections allowance on this server.