Проблеми «довгих» скриптів php

Проблеми «довгих» скриптів php
Іноді виникає необхідність писати скрипти, робота яких займає тривалий час. Наприклад, скрипти створення / розгортання резервних копій, установки демо-версії якогось додатку, агрегування великих обсягів даних, імпорту / експорту даних і т.п. Для того, щоб такі скрипти не припиняли свою роботу в несподіваний момент, потрібно знати і пам'ятати про деякі речі.

зовнішній таймаут

В першу чергу потрібно встановити відповідне значення параметра max_execution_time в конфіги PHP.

Якщо скрипт запускається веб-сервером (тобто у відповідь на HTTP-запит від користувача), то слід також правильно налаштувати параметри таймаута в конфіги веб-сервера. Для apache це параметри TimeOut і FastCgiServer ... -idle-timeout. (Якщо PHP працює через FastCGI), для nginx send_timeout і fastcgi_read_timeout (якщо PHP працює через FastCGI).

Веб-сервер може також проксіровать запити на інший веб-сервер, який і запустить PHP скрипт (не рідкий приклад, nginx - фронтенд, apache - бекенда). У цьому випадку на проксірующем веб-сервері необхідно також налаштовувати таймаут проксінг. Для apache ProxyTimeout. для nginx proxy_read_timeout.

переривання користувачем

Якщо скрипт запускається у відповідь на HTTP-запит, то користувач може зупинити виконання запиту в своєму браузері, в цьому випадку припинить свою роботу і PHP скрипт. Якщо ж потрібно, щоб скрипт продовжив свою роботу навіть після зупинки запиту, встановіть в TRUE параметр ignore_user_abort в конфіги PHP.

Втрата відкритих з'єднань

Якщо в скрипті відкривається з'єднання з будь-яким сервісом / службою (з БД, з поштовим сервером, з FTP-сервером.), І під час виконання скрипта деякий час з'єднання не використовується, то воно може бути закрито цим сервісом. Наприклад, якщо під час роботи скрипта деякий час не виконувати запити до MySQL, то MySQL закриє з'єднання через час, заданий в параметрі wait_timeout. Як наслідок, при спробі виконати черговий запит виникне помилка.

У таких випадках слід перевіряти активність сполуки, в тих місцях коду, де можливі простої його використання, і перепідключатися при необхідності. Наприклад в модулі MySQLi є корисна функція mysqli :: ping для перевірки активності сполуки, а також параметр конфігурації mysqli.reconnect для автоматичного перепідключення, при розриві з'єднання. При відсутності подібних функцій для інших видів з'єднань, можна спробувати написати її самому. У ній потрібно тривіальним чином звернутися до сервісу (наприклад, виконати запит SELECT 1 FROM dual), і в разі помилки (відловити за допомогою try ... catch.) Перепідключитися.

паралельний запуск

Нерідко довгі скрипти запускаються за розкладом (по cron), і очікується, що в один момент часу буде працювати тільки одна копія скрипта. Але може трапитися так, що черговий запуск скрипта відбудеться раніше, ніж закінчить роботу попередній, і як правило це небажано (двічі імпортуються одні й ті ж дані, затруться дані використовуються першим скриптом.).

У таких випадках можна використовувати блокування використовуваних ресурсів, але ця задача завжди вирішується індивідуально. Або можна просто перевіряти, чи не запущена чи інша копія цього скрипта, і або почекати завершення його роботи, або завершити поточний запуск. Для цього можна переглядати список запущених процесів, або використовувати блокування запуску самого скрипта, що щось на кшталт:

Навантаження на веб-сервер

У випадках, коли довгі скрипти запускаються через веб-сервер, з'єднання клієнта з цим самим веб-сервером залишається відкритим до тих пір, поки не відпрацює скрипт. Це не є добре, тому що задача веб-сервера якнайшвидше опрацювати запит і віддати результат. Якщо ж з'єднання залишається висіти, то один з Воркер (процесів) веб-сервера на довгий час буде зайнятий. А якщо одночасно буде запущено досить багато таких скриптів, то вони можуть зайняти всі (ну або майже все) вільні Воркер (для apache см. MaxClients), і веб-сервер просто не зможе обробляти інші запити.

Тому слід при обробці запиту користувача, запускати скрипт в фоновому режимі через php-cli, щоб не навантажувати веб-сервер, а користувачеві відповідати що його запит обробляється. При необхідності можна періодично перевіряти стан обробки за допомогою AJAX запитів.

Ось, мабуть, і все що я можу розповісти по цій темі. Сподіваюся, для кого-то буде корисним.

Схожі статті