diff --git a/docs/configuration-reference.md b/docs/configuration-reference.md index 41365b74..c4a391b7 100644 --- a/docs/configuration-reference.md +++ b/docs/configuration-reference.md @@ -89,4 +89,11 @@ swoole: package_max_length: 8388608 # in bytes, 8388608b = 8 MiB + + worker_max_request: 0 + # integer >= 0, indicates the number of requests after which a worker reloads automatically + # This can be useful to limit memory leaks + worker_max_request_grace: ~ + # 'grace period' for worker reloading. If not set, default is worker_max_request / 2. Worker reloads + # after 'worker_max_request + rand(0,worker_max_request_grace)' requests ``` diff --git a/src/Bridge/Symfony/Bundle/Command/AbstractServerStartCommand.php b/src/Bridge/Symfony/Bundle/Command/AbstractServerStartCommand.php index 0a5d2100..e2843ec8 100644 --- a/src/Bridge/Symfony/Bundle/Command/AbstractServerStartCommand.php +++ b/src/Bridge/Symfony/Bundle/Command/AbstractServerStartCommand.php @@ -202,6 +202,8 @@ protected function prepareConfigurationRowsToPrint(HttpServerConfiguration $serv ['running_mode', $serverConfiguration->getRunningMode()], ['worker_count', $serverConfiguration->getWorkerCount()], ['reactor_count', $serverConfiguration->getReactorCount()], + ['worker_max_request', $serverConfiguration->getMaxRequest()], + ['worker_max_request_grace', $serverConfiguration->getMaxRequestGrace()], ['memory_limit', format_bytes(get_max_memory())], ['trusted_hosts', \implode(', ', $runtimeConfiguration['trustedHosts'])], ]; diff --git a/src/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php b/src/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php index a5314f9e..63e01bc8 100644 --- a/src/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php +++ b/src/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php @@ -211,6 +211,13 @@ public function getConfigTreeBuilder(): TreeBuilder ->scalarNode('task_worker_count') ->defaultNull() ->end() + ->integerNode('worker_max_request') + ->min(0) + ->defaultValue(0) + ->end() + ->scalarNode('worker_max_request_grace') + ->defaultNull() + ->end() ->end() ->end() // settings ->end() diff --git a/src/Server/HttpServerConfiguration.php b/src/Server/HttpServerConfiguration.php index 4ab2e1d9..84434d3a 100644 --- a/src/Server/HttpServerConfiguration.php +++ b/src/Server/HttpServerConfiguration.php @@ -25,6 +25,8 @@ class HttpServerConfiguration private const SWOOLE_HTTP_SERVER_CONFIG_PID_FILE = 'pid_file'; private const SWOOLE_HTTP_SERVER_CONFIG_BUFFER_OUTPUT_SIZE = 'buffer_output_size'; private const SWOOLE_HTTP_SERVER_CONFIG_PACKAGE_MAX_LENGTH = 'package_max_length'; + private const SWOOLE_HTTP_SERVER_CONFIG_WORKER_MAX_REQUEST = 'worker_max_request'; + private const SWOOLE_HTTP_SERVER_CONFIG_WORKER_MAX_REQUEST_GRACE = 'worker_max_request_grace'; /** * @todo add more @@ -44,6 +46,8 @@ class HttpServerConfiguration self::SWOOLE_HTTP_SERVER_CONFIG_BUFFER_OUTPUT_SIZE => 'buffer_output_size', self::SWOOLE_HTTP_SERVER_CONFIG_PACKAGE_MAX_LENGTH => 'package_max_length', self::SWOOLE_HTTP_SERVER_CONFIG_TASK_WORKER_COUNT => 'task_worker_num', + self::SWOOLE_HTTP_SERVER_CONFIG_WORKER_MAX_REQUEST => 'max_request', + self::SWOOLE_HTTP_SERVER_CONFIG_WORKER_MAX_REQUEST_GRACE => 'max_request_grace', ]; private const SWOOLE_SERVE_STATIC = [ @@ -82,6 +86,8 @@ class HttpServerConfiguration * - public_dir (default: '%kernel.root_dir%/public') * - buffer_output_size (default: '2097152' unit in byte (2MB)) * - package_max_length (default: '8388608b' unit in byte (8MB)) + * - worker_max_requests: Number of requests after which the worker reloads + * - worker_max_requests_grace: Max random number of requests for worker reloading * * @throws \Assert\AssertionFailedException */ @@ -200,6 +206,16 @@ public function getServerSocket(): Socket return $this->sockets->getServerSocket(); } + public function getMaxRequest(): int + { + return $this->settings[self::SWOOLE_HTTP_SERVER_CONFIG_WORKER_MAX_REQUEST]; + } + + public function getMaxRequestGrace(): ?int + { + return $this->settings[self::SWOOLE_HTTP_SERVER_CONFIG_WORKER_MAX_REQUEST_GRACE] ?? null; + } + /** * @throws \Assert\AssertionFailedException */ @@ -256,11 +272,30 @@ public function getSwooleEnableStaticHandler(): bool return self::SWOOLE_SERVE_STATIC[$this->settings[self::SWOOLE_HTTP_SERVER_CONFIG_SERVE_STATIC]]; } + /** + * @see getSwooleSettings() + */ public function getSwooleDocumentRoot(): ?string { return 'default' === $this->settings[self::SWOOLE_HTTP_SERVER_CONFIG_SERVE_STATIC] ? $this->settings[self::SWOOLE_HTTP_SERVER_CONFIG_PUBLIC_DIR] : null; } + /** + * @see getSwooleSettings() + */ + public function getSwooleMaxRequest(): int + { + return $this->settings[self::SWOOLE_HTTP_SERVER_CONFIG_WORKER_MAX_REQUEST] ?? 0; + } + + /** + * @see getSwooleSettings() + */ + public function getSwooleMaxRequestGrace(): ?int + { + return $this->settings[self::SWOOLE_HTTP_SERVER_CONFIG_WORKER_MAX_REQUEST_GRACE] ?? null; + } + /** * @throws \Assert\AssertionFailedException */ @@ -363,6 +398,16 @@ private function validateSetting(string $key, $value): void Assertion::integer($value, \sprintf('Setting "%s" must be an integer.', $key)); Assertion::greaterThan($value, 0, 'Count value cannot be negative, "%s" provided.'); + break; + case self::SWOOLE_HTTP_SERVER_CONFIG_WORKER_MAX_REQUEST: + Assertion::integer($value, \sprintf('Setting "%s" must be an integer.', $key)); + Assertion::greaterOrEqualThan($value, 0, 'Value cannot be negative, "%s" provided.'); + + break; + case self::SWOOLE_HTTP_SERVER_CONFIG_WORKER_MAX_REQUEST_GRACE: + Assertion::nullOrInteger($value, \sprintf('Setting "%s" must be an integer or null.', $key)); + Assertion::nullOrGreaterOrEqualThan($value, 0, 'Value cannot be negative, "%s" provided.'); + break; default: return;