Каждый хоть раз писал такой PHP-скрипт, после запуска которого сервер начинал реветь как турбина, а база данных падала замертво. Обычно это «генерация отчёта за год», «импорт из 500-мегабайтного Excel-а» или «API, который возвращает всё и сразу».
Фокус в том, что чудес не бывает. Если ты гоняешь тяжелый процесс прямо в запросе, пользователю будет больно, серверу будет больно, и тебе тоже. Поэтому надо научиться выносить дичь в фон, кэшировать, дробить или тупо не писать говнокод.
1. Очереди задач: Redis или Beanstalkd через Pheanstalk
Redis (быстро и просто)
Redis часто уже стоит в проекте, и через predis/predis можно легко построить очередь. Это нормальный вариант, если у тебя нет Beanstalkd.
$redis->rpush('task_queue', json_encode(['task' => 'report']));
Beanstalkd (через Pheanstalk)
Если хочешь реально лёгкий сервер для очередей — бери Beanstalkd. Он минималистичный и очень быстрый. В PHP используем Pheanstalk:
composer require pda/pheanstalk
Постановка задачи:
use Pheanstalk\Pheanstalk;
$pheanstalk = Pheanstalk::create('127.0.0.1');
$pheanstalk
->useTube('default')
->put(json_encode(['task' => 'send_email', 'user_id' => 42]));
Воркер:
$pheanstalk = Pheanstalk::create('127.0.0.1');
while ($job = $pheanstalk->watch('default')->reserve()) {
$data = json_decode($job->getData(), true);
echo "Выполняю: {$data['task']} для юзера {$data['user_id']}\n";
// твоя тяжёлая логика
sleep(3);
$pheanstalk->delete($job);
}2. Кэш: не считай дважды
Если у тебя есть операция «полчаса считает, секунду показывает» — кэшируй результат. Файлы, Redis, Memcached — не важно. Важно, что ты не будешь каждый раз долбить базу и молиться.
Простейший файловый кэш:
$cacheFile = 'cache/report.txt';
if (file_exists($cacheFile) && (filemtime($cacheFile) > (time() - 3600))) {
$data = file_get_contents($cacheFile);
} else {
$data = performHeavyOperation();
file_put_contents($cacheFile, $data);
}
Вместо того, чтобы снова и снова гонять «тяжёлый отчёт за месяц», ты один раз посчитал и дальше читаешь готовое.
3. Асинхронка: форкнись или умри
PHP по дефолту однопоточный дегенерат. Но если ты юзаешь pcntl_fork, можно хотя бы распараллелить.
if (function_exists('pcntl_fork')) {
$pid = pcntl_fork();
if ($pid == 0) {
// Дочерний процесс
performHeavyOperation();
exit;
}
}
Или даже тупее: дергай сам себя через curl в фоне (exec('php script.php > /dev/null &')). Это не rocket science, но серверу станет легче.
4. Оптимизация кода: перестань писать дичь
Самое неприятное, но и самое честное. Часто проблема не в железе и не в том, что «PHP медленный», а в том, что ты написал чудовище.
— Сократи количество SQL-запросов.
— Юзай chunk() вместо того, чтобы тащить 10 млн строк в память.
— Не пихай бесконечные циклы с file_get_contents() в прод.
Иногда переписать пару запросов дешевле, чем прикручивать кластер RabbitMQ.
5. Масштабирование: когда всё остальное не спасает
Если ты сделал всё выше, но сервер всё равно стонет — пора доставать кошелёк. Горизонтальное масштабирование, несколько воркеров, облако — да, это больно, но иногда без вариантов.
Итог
Есть пять реальных способов не убить сервер тяжёлым PHP-скриптом:
- Очереди.
- Кэш.
- Асинхронка.
- Оптимизация.
- Масштабирование.
Если ты думаешь «может, я просто поставлю sleep(1) в цикле, и будет норм?» — нет, не будет.
0 комментариев