Долгая обработка запроса php как передать браузеру что бы ждал ответа?

Доброе время суток, у меня встала задача сформировать файл в режиме реального времени на загрузку по запросу.
Все работает но беда в том что обработка запроса занимает очень много времени больше 20 минут
Сразу скажу что это API ни каких js тут предлагать не надо.
Во общем суть в том что я пытаюсь клиенту ( в моем случае это обычный браузер ) сказать что обработка займет много времени пытаясь отправить ему код ответа 102 - но код ответа не приходит в запросе.
Я погуглил и мне сказали что нужно добавить 202 как окончательный код ответа "сотки" не входят в этот список поэтому не работает.
Моя логика проста я проверяю http аутентификацию
И пытаюсь сказать заголовками: чувак твой запрос займет много времени не закрывай соединение а просто жди
И типо я твой запрос принял успешно
Далее я пытаюсь отправить заголовки браузеру сразу вызвав ob_flush();
Браузер тупит пару минут но потом закрывает соединение, в дебаге я вижу что он получил код 202 но не где не вижу 102
В итоге обработка не завершена потому что браузер закрыл соединение - как это победить?

header('Cache-Control: no-cache, must-revalidate, max-age=0');
        header('HTTP/1.1 102 Processing');
        $has_supplied_credentials = !(empty($_SERVER['PHP_AUTH_USER']) && empty($_SERVER['PHP_AUTH_PW']));
        $is_not_authenticated = (
            !$has_supplied_credentials ||
            $_SERVER['PHP_AUTH_USER'] != $AUTH_USER ||
            $_SERVER['PHP_AUTH_PW']   != $AUTH_PASS
        );
        if ($is_not_authenticated) {
            header('HTTP/1.1 401 Authorization Required');
            header('WWW-Authenticate: Basic realm="Access denied"');
            exit;
        }
        header('HTTP/1.1 102 Processing');
        header('HTTP/1.1 202 Accepted');
        ob_flush();
        set_time_limit(0);
        //долгая обработка запроса
        sleep(60*30);

        $out = fopen('php://output', 'w');
        header('HTTP/1.1 200 OK');
        header("Content-Type: application/csv");
        header("Content-Disposition:attachment;filename=data.csv");
        foreach ($text as $string){
            fputcsv($out, $string);
        }
        fclose($out);


UPDATE:
Всем спасибо кто принял участие в этом вопросе, правильный ответ дал Antonio Solo
PS: Во общем я немного не так изложил проблему, я прекрасно знаю про задачи менеджеры очередей и т.д. который помогают решат подобные проблемы, беда в том что это не подходит для моей задачи, мне нужно для 2 - 3 человек пару раз в сутки в режиме реального времени выгрузить данные и сделать это за один запрос потмоу что для простых клиентов вроде Power BI или wget нет возможности применять ajax или сложную систему редиректов.

Я решил проблему как и сказал Antonio Solo я отдавал файл по ходу его обработки, моей ошибкой было что я пытался собрать файл а потом отдать его клиенту. но если отдавать файл кусками то все работает как надо.

перед тем как отдать файл я разумеется сделал

header("Content-Type: application/csv");
        header("Content-Disposition:attachment;filename=data.csv");
        header('HTTP/1.1 102 Processing');
        header('HTTP/1.1 202 Accepted');
        ob_end_flush();
        set_time_limit(0);
        $out = fopen('php://output', 'w');
        while(){
                  // тут тяжолая обработка файла
                  fputcsv($out, $text[$i]); // в моем случае это csv поэтому я отдаю ее построчно
         }
        fclose($out); // на этом все


Таким образом, браузер как и любой другой http клиент не разрывает содеинение и ожидает завершения передачи данных, и делается это в 1 запрос.

Я хочу отметить что это решение подходит долеко не для всех задач так как может вызвать большую загрузку сервера ( если например 100 человек разом откроют соединения и т.д. ) - но конкретно в моей задачи это решение устраеват на 100%

Надеюсь моей кометарий поможет людям который столкнулись с такой же задачей.
  • Вопрос задан
  • 1433 просмотра
Решения вопроса 1
solotony
@solotony
покоряю пик Балмера
отключи проксирование/буферизацию и отдавай ему контент порциями. у меня так по несколько часов работали скрипты.
Ответ написан
Пригласить эксперта
Ответы на вопрос 3
irishmann
@irishmann
Научись пользоваться дебаггером
У меня такие тяжелые процессы вынесены в отдельный сервис. Пользователь создает задачу, все это записываю в БД, сервис запускается по крону, смотрит наличие новых задач, выполняет, отмечает что выполнил и откуда можно забрать результат. У пользователя в интерфейсе висит сообщение что задача обрабатывается. Он может пойти погулять где-то, потом прийти и посмотреть результат и скачать файл.
Ответ написан
402d
@402d
начинал с бейсика на УКНЦ в 1988
1. контролер возвращает
Для Вас начата обработка номер бла-бла.
2. Броузер потом дергает по таймауту . например раз в 15 секунд.
урл проверки статуса работы
wait/ok
3. при получении успеха
идет на третий урл получения результата по номеру таска.
Ответ написан
neuotq
@neuotq
Прокрастинация
Ну тут если делать всё прямо, то это тупо висящая страничка. Опасность еще в том, что пользователь в любой момент может нажать обновить страницу например. В любом случае, в прямом случаи вам нужно смотреть настройки таймаута длительности запроса в настройках php и вашего веб сервера.
А так.. даже не знаю, может стоит проверять что запрос будет долгий, и если запрос будет долгий, вначале редиректить на временную страницу с предупреждением, а оттуда с таймаут уже на запрос, чтобы у пользователи перед глазами висела надпись ждите и ничего не трогайте.
Это я описал алгоритм без js, со временной страницы можно редирект делать через
.
А так... всё же лучше делать отдельно задания.
Те пользователь отправил вам тяжелый запрос, вы его приняли, сразу вернули пользователю ответ что я принял задание, будет готово через 20 минут. А в коде в расписание/очередь свыставляете задание на генерацию вашей csv.
А далее пользователь получает(пуш уведомление. email еще как, это не суть) уведомление о готовности задания, либо о его не удачи.
Ну те вам нужен: очередь/планировщик задания, события, уведомления.
Я бы на вашем месте всё же рассмотрел либо фреймворк какой-то, либо комопненты Симфони или в репозитории composer поискал что-то, для облегчения задачи. Чтобы меньше велосипедов плодить.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы