Skip to content

Commit 4c8185a

Browse files
Merge pull request #28 from PHPFastCGI/shutdown
Auto Shutdown
2 parents 57fcfed + b453541 commit 4c8185a

File tree

14 files changed

+88
-64
lines changed

14 files changed

+88
-64
lines changed

src/Command/DaemonRunCommand.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public function __construct(KernelInterface $kernel, DriverContainerInterface $d
5959
->addOption('request-limit', null, InputOption::VALUE_OPTIONAL, 'The maximum number of requests to handle before shutting down')
6060
->addOption('memory-limit', null, InputOption::VALUE_OPTIONAL, 'The memory limit on the daemon instance before shutting down')
6161
->addOption('time-limit', null, InputOption::VALUE_OPTIONAL, 'The time limit on the daemon in seconds before shutting down')
62+
->addOption('auto-shutdown', null, InputOption::VALUE_NONE, 'Perform a graceful shutdown after receiving a 5XX HTTP status code')
6263
->addOption('driver', null, InputOption::VALUE_OPTIONAL, 'The implementation of the FastCGI protocol to use', 'userland');
6364
}
6465

@@ -78,12 +79,14 @@ private function getDaemonOptions(InputInterface $input, OutputInterface $output
7879
$requestLimit = $input->getOption('request-limit') ?: DaemonOptions::NO_LIMIT;
7980
$memoryLimit = $input->getOption('memory-limit') ?: DaemonOptions::NO_LIMIT;
8081
$timeLimit = $input->getOption('time-limit') ?: DaemonOptions::NO_LIMIT;
82+
$autoShutdown = $input->getOption('auto-shutdown');
8183

8284
return new DaemonOptions([
8385
DaemonOptions::LOGGER => $logger,
8486
DaemonOptions::REQUEST_LIMIT => $requestLimit,
8587
DaemonOptions::MEMORY_LIMIT => $memoryLimit,
8688
DaemonOptions::TIME_LIMIT => $timeLimit,
89+
DaemonOptions::AUTO_SHUTDOWN => $autoShutdown,
8790
]);
8891
}
8992

src/DaemonInterface.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ public function run();
5151
/**
5252
* Flag the daemon for shutting down. This will stop it from accepting
5353
* requests.
54+
*
55+
* @param string|null Optional message.
5456
*/
55-
public function flagShutdown();
57+
public function flagShutdown($message = null);
5658
}

src/DaemonOptions.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class DaemonOptions
1717
const REQUEST_LIMIT = 'request-limit';
1818
const MEMORY_LIMIT = 'memory-limit';
1919
const TIME_LIMIT = 'time-limit';
20+
const AUTO_SHUTDOWN = 'auto-shutdown';
2021

2122
/**
2223
* @var array
@@ -44,6 +45,7 @@ public function __construct(array $options = [])
4445
self::REQUEST_LIMIT => self::NO_LIMIT,
4546
self::MEMORY_LIMIT => self::NO_LIMIT,
4647
self::TIME_LIMIT => self::NO_LIMIT,
48+
self::AUTO_SHUTDOWN => false,
4749
];
4850

4951
foreach ($options as $option => $value) {

src/DaemonTrait.php

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@
22

33
namespace PHPFastCGI\FastCGIDaemon;
44

5-
use PHPFastCGI\FastCGIDaemon\Exception\MemoryLimitException;
6-
use PHPFastCGI\FastCGIDaemon\Exception\RequestLimitException;
75
use PHPFastCGI\FastCGIDaemon\Exception\ShutdownException;
8-
use PHPFastCGI\FastCGIDaemon\Exception\TimeLimitException;
96

107
trait DaemonTrait
118
{
@@ -14,6 +11,11 @@ trait DaemonTrait
1411
*/
1512
private $isShutdown = false;
1613

14+
/**
15+
* @var string
16+
*/
17+
private $shutdownMessage = '';
18+
1719
/**
1820
* @var int
1921
*/
@@ -29,12 +31,20 @@ trait DaemonTrait
2931
*/
3032
private $memoryLimit;
3133

34+
/**
35+
* @var bool
36+
*/
37+
private $autoShutdown;
38+
3239
/**
3340
* Flags the daemon for shutting down.
41+
*
42+
* @param string $message Optional shutdown message
3443
*/
35-
public function flagShutdown()
44+
public function flagShutdown($message = null)
3645
{
3746
$this->isShutdown = true;
47+
$this->shutdownMessage = (null === $message ? 'Daemon flagged for shutdown' : $message);
3848
}
3949

4050
/**
@@ -48,6 +58,7 @@ private function setupDaemon(DaemonOptions $daemonOptions)
4858
$this->requestCount = 0;
4959
$this->requestLimit = $daemonOptions->getOption(DaemonOptions::REQUEST_LIMIT);
5060
$this->memoryLimit = $daemonOptions->getOption(DaemonOptions::MEMORY_LIMIT);
61+
$this->autoShutdown = $daemonOptions->getOption(DaemonOptions::AUTO_SHUTDOWN);
5162

5263
$timeLimit = $daemonOptions->getOption(DaemonOptions::TIME_LIMIT);
5364

@@ -59,13 +70,22 @@ private function setupDaemon(DaemonOptions $daemonOptions)
5970
}
6071

6172
/**
62-
* Increments the request count.
73+
* Increments the request count and looks for application errors.
6374
*
64-
* @param int $number The number of requests to increment the count by
75+
* @param int[] $statusCodes The status codes of sent responses
6576
*/
66-
private function incrementRequestCount($number)
77+
private function considerStatusCodes($statusCodes)
6778
{
68-
$this->requestCount += $number;
79+
$this->requestCount += count($statusCodes);
80+
81+
if ($this->autoShutdown) {
82+
foreach ($statusCodes as $statusCode) {
83+
if ($statusCode >= 500 && $statusCode < 600) {
84+
$this->flagShutdown('Automatic shutdown following status code: ' . $statusCode);
85+
break;
86+
}
87+
}
88+
}
6989
}
7090

7191
/**
@@ -83,7 +103,7 @@ private function installSignalHandlers()
83103
});
84104

85105
pcntl_signal(SIGALRM, function () {
86-
throw new TimeLimitException('Daemon time limit reached (received SIGALRM)');
106+
throw new ShutdownException('Daemon time limit reached (received SIGALRM)');
87107
});
88108
}
89109

@@ -97,22 +117,22 @@ private function installSignalHandlers()
97117
private function checkDaemonLimits()
98118
{
99119
if ($this->isShutdown) {
100-
throw new ShutdownException('Daemon flagged for shutdown');
120+
throw new ShutdownException($this->shutdownMessage);
101121
}
102122

103123
pcntl_signal_dispatch();
104124

105125
if (DaemonOptions::NO_LIMIT !== $this->requestLimit) {
106126
if ($this->requestLimit <= $this->requestCount) {
107-
throw new RequestLimitException('Daemon request limit reached ('.$this->requestCount.' of '.$this->requestLimit.')');
127+
throw new ShutdownException('Daemon request limit reached ('.$this->requestCount.' of '.$this->requestLimit.')');
108128
}
109129
}
110130

111131
if (DaemonOptions::NO_LIMIT !== $this->memoryLimit) {
112132
$memoryUsage = memory_get_usage(true);
113133

114134
if ($this->memoryLimit <= $memoryUsage) {
115-
throw new MemoryLimitException('Daemon memory limit reached ('.$memoryUsage.' of '.$this->memoryLimit.' bytes)');
135+
throw new ShutdownException('Daemon memory limit reached ('.$memoryUsage.' of '.$this->memoryLimit.' bytes)');
116136
}
117137
}
118138
}

src/Driver/Userland/ConnectionHandler/ConnectionHandler.php

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,17 @@ public function ready()
8585
$this->buffer .= $data;
8686
$this->bufferLength += $dataLength;
8787

88-
$dispatchedRequests = 0;
88+
$statusCodes = [];
8989

9090
while (null !== ($record = $this->readRecord())) {
91-
$dispatchedRequests += $this->processRecord($record);
91+
$statusCode = $this->processRecord($record);
92+
93+
if (null != $statusCode) {
94+
$statusCodes[] = $statusCode;
95+
}
9296
}
9397

94-
return $dispatchedRequests;
98+
return $statusCodes;
9599
} catch (\Exception $exception) {
96100
$this->close();
97101

@@ -198,17 +202,16 @@ private function processRecord(array $record)
198202
if (null !== $content) {
199203
fwrite($this->requests[$requestId]['stdin'], $content);
200204
} else {
201-
$this->dispatchRequest($requestId);
202-
203-
return 1; // One request was dispatched
205+
// Returns the status code
206+
return $this->dispatchRequest($requestId);
204207
}
205208
} elseif (DaemonInterface::FCGI_ABORT_REQUEST === $record['type']) {
206209
$this->endRequest($requestId);
207210
} else {
208211
throw new ProtocolException('Unexpected packet of type: '.$record['type']);
209212
}
210213

211-
return 0; // Zero requests were dispatched
214+
return null; // No status code to return
212215
}
213216

214217
/**
@@ -345,7 +348,7 @@ private function writeRecord($requestId, $type, $content = null)
345348
* Write a response to the connection as FCGI_STDOUT records.
346349
*
347350
* @param int $requestId The request id to write to
348-
* @param string $headerData The header data to write (including terminating CRLFCRLF)
351+
* @param string $headerData The header -data to write (including terminating CRLFCRLF)
349352
* @param StreamInterface $stream The stream to write
350353
*/
351354
private function writeResponse($requestId, $headerData, StreamInterface $stream)
@@ -399,6 +402,9 @@ private function dispatchRequest($requestId)
399402
}
400403

401404
$this->endRequest($requestId);
405+
406+
// This method exists on PSR-7 and Symfony responses
407+
return $response->getStatusCode();
402408
}
403409

404410
/**

src/Driver/Userland/ConnectionHandler/ConnectionHandlerInterface.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ interface ConnectionHandlerInterface
1414
* Triggered when the connection the handler was assigned to is ready to
1515
* be read.
1616
*
17-
* @return int The number of requests dispatched during the function call
17+
* @return int[] The status codes of requests dispatched during the function call
1818
*/
1919
public function ready();
2020

src/Driver/Userland/UserlandDaemon.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ private function processConnectionPool()
103103
}
104104

105105
try {
106-
$dispatchedRequests = $this->connectionHandlers[$id]->ready();
107-
$this->incrementRequestCount($dispatchedRequests);
106+
$statusCodes = $this->connectionHandlers[$id]->ready();
107+
$this->considerStatusCodes($statusCodes);
108108
} catch (UserlandDaemonException $exception) {
109109
$this->daemonOptions->getOption(DaemonOptions::LOGGER)->error($exception->getMessage());
110110
}

src/Exception/MemoryLimitException.php

Lines changed: 0 additions & 11 deletions
This file was deleted.

src/Exception/RequestLimitException.php

Lines changed: 0 additions & 11 deletions
This file was deleted.

src/Exception/TimeLimitException.php

Lines changed: 0 additions & 11 deletions
This file was deleted.

0 commit comments

Comments
 (0)