Environment Setup
- Built using
swoole:alpine
Docker image
FROM phpswoole/swoole:php7.4-alpine
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && cat /etc/apk/repositories
# Quick PHP extension installation
COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/
RUN install-php-extensions pcntl redis pdo_mysql
WORKDIR /var/www
COPY . .
RUN chmod -R 0777 storage && \
chmod -R 0777 bootstrap/cache && \
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/ && \
composer install --optimize-autoloader --no-dev && \
php artisan config:cache && \
php artisan route:cache && \
php artisan view:cache && \
php artisan laravels publish --no-interaction
CMD ["php", "bin/laravels", "start", "--env=product"]
- For initial setup, comment out the last two lines in Dockerfile:
docker build . -t test-image
docker run -d -it -p 80:5200 -v [/mnt/d/xxxx]:/var/dev --name=test-service -w /var/dev test-image /bin/sh
docker exec -it memory-service /bin/sh
Error Logs
[2022-01-01 02:29:45 $19.0] WARNING Server::check_worker_exit_status(): worker(pid=1641, id=5) abnormal exit, status=255, signal=0
[2022-01-01 02:30:24 *1642.0] ERROR php_swoole_server_rshutdown() (ERRNO 503): Fatal error: Allowed memory size of 134217728 bytes exhausted...
... (truncated for brevity)
Official Documentation Guidance
Memory Leak Prevention
- Avoid global variables or manually clean them
- Prevent infinite appends to static variables/singletons
// Bad practice example
class Test {
public static $array = [];
public static $string = '';
}
Memory Leak Detection
- Modify
config/laravels.php
:'worker_num' => 1, 'max_request' => 1000000,
- Add debug route:
Route::get('/debug-memory-leak', function () { global $previous; $current = memory_get_usage(); $stats = [ 'prev_mem' => $previous, 'curr_mem' => $current, 'diff_mem' => $current - $previous, ]; $previous = $current; return $stats; });
- Monitor memory changes using:
watch -n 1 curl 127.0.0.1:5200/debug-memory-leak
Using Swoole Tracker 3.1
- Install free version following official guide
- Add tracker hooks:
trackerHookMalloc();
- Analyze leaks:
php -r "trackerAnalyzeLeak();"
Sample Leak Report
[29260 (Loop 4252)] /var/dev/vendor/facade/ignition/src/QueryRecorder/QueryRecorder.php:44 => [-192]
... (truncated)
Root Cause Analysis
The memory leak originated from repeated command registration in AdminServiceProvider
:
public function register()
{
// Problematic code
$this->commands($this->commands);
if (config('app.debug')) {
$this->commands($this->devCommands);
}
}
Each request appended commands to Artisan::$bootstrappers
, causing memory growth:
public static function starting(Closure $callback)
{
static::$bootstrappers[] = $callback; // Memory leak source
}
Solution
- Remove unnecessary service provider registration in
laravels.register_providers
- Implement proper cleanup mechanisms
- Use
max_request
as safety net:'max_request' => 500, // Handle 500 requests per worker
Final Docker CMD:
CMD ["php", "bin/laravels", "start", "--env=product"]