Почему серверный скрипт не останавливается, когда EventSource закрыт? - PullRequest
3 голосов
/ 17 апреля 2020

TL; DR обновление: Независимо от того, закрываете ли вы EventSource от клиента или полностью закрываете клиент, php продолжает выполняться на бэкэнде и не может сообщить правильное значение для connection_aborted(). Почему это может быть?

Я был по всему Google, и Stack Overflow искал ответы на этот вопрос, но ни одно из предложенных исправлений не решает проблему: у меня есть простая страница, проверяющая функциональность событий, отправляемых сервером, используя JavaScript и php. Все работало хорошо, пока я не понял, что выполнение серверного скрипта не прекращалось, когда клиент переходил на другую страницу или обновлялся. Похоже, это общая проблема, и предложения, приведенные в других вопросах, не принесли мне никакой пользы.

Вопросы StackOverflow, которые я уже исследовал

Связанные статьи и другие материалы, которые я уже исследовал

Я удалил код всего, что, по моему мнению, могло быть возможным виновником, и у меня все еще есть проблема. Я особенно удивлен тем, что connection_aborted продолжает сообщать false после явного вызова EventSource.close() в клиенте или простого закрытия клиента до завершения 10-секундного сервера l oop. Вот мой точный код, после удаления всего, кроме отправленных сервером событий:

sse_tests. js

document.addEventListener('DOMContentLoaded', () => {
  // Set up EventSource for receiving server-sent events.
  const testEventSource = new EventSource('sse_tests.php');
  testEventSource.addEventListener('test', (e) => {
    const data = JSON.parse(e.data);
    console.log(`count: ${data.count}`);
    if (data.count >= 5) {
      testEventSource.close();
    }
  });
});

sse_tests. php

<?php
// Set the event stream header(s).
header("Cache-Control: no-cache");
header("Content-Type: text/event-stream");

// XXX Override automatic detection of client abortion; we'll do it ourselves.
// (This was suggested in another answer, and I have the same issue with or without it)
ignore_user_abort(true);

// Initialize an arbitrary count parameter to investigate client communication.
$count = 1;

while ($count <= 10) {
  $eventData = json_encode(array(
    "count" => $count,
  ));

  echo "event: test\n";
  echo "data: ${eventData}";
  echo "\n\n";

  ob_flush();
  flush();

  $aborted = connection_aborted();

  error_log("count: ${count}, connection_aborted: ${aborted}");

  if ($aborted) {
    break;
  }

  $count++;

  sleep(1);
}

Клиент успешно открывает соединение, отслеживает его на 5 излучений события test и затем перестает видеть дальнейшие выбросы события test, но сервер продолжает выполнить для полного счета 10, даже после вызова testEventSource.close() или закрытия окна браузера до полного счета 10, о чем свидетельствует содержимое журнала сервера здесь:

count: 1, connection_aborted: 0
count: 2, connection_aborted: 0
count: 3, connection_aborted: 0
count: 4, connection_aborted: 0
count: 5, connection_aborted: 0
count: 6, connection_aborted: 0
count: 7, connection_aborted: 0
count: 8, connection_aborted: 0
count: 9, connection_aborted: 0
count: 10, connection_aborted: 0

I ' м на виртуальном хостинге с php 7.2 и минимальными настройками конфигурации сервера. Дайте мне знать, может ли это стать источником раздоров, и я постараюсь изучить дополнительные конфигурации по умолчанию и поделиться всем, что нужно.

1 Ответ

0 голосов
/ 27 апреля 2020

Основываясь на обновленной информации, я считаю, что это определенно неправильно:

ignore_user_abort(true);

Это должно быть:

ignore_user_abort(false);

... чтобы отключение не игнорировалось. То, что происходит после этого, является чем-то загадочным. MDN сообщает, что соединение является постоянным, поэтому это может быть ошибка в браузере, которая не закрывает соединение. Это может быть проблема с балансировщиком нагрузки или обратным прокси, если таковые имеются. Это может быть проблема в веб-сервере. Или это может быть проблема в конфигурации PHP.

В чате кажется, что тестовый сервер, скорее всего, nginx с PHP, работающим под FastCGI. Вы используете Apache с mod_ php вместо этого? Если это так, то все еще происходит, когда вы запускаете PHP как CGI? Это должно помочь в дальнейшей изоляции проблемы.

Я бы также использовал connection_status (), чтобы вы могли быть более уверены в состоянии соединения. https://www.php.net/manual/en/features.connection-handling.php

...