Как выполнить задачи, связанные с запросом, после возврата ответа страницы. Действительно ли hook exit() делает это?


Я пытаюсь выяснить, возможно ли "скрыть" предполагаемое время выполнения некоторых задач ведения журнала в конце запроса, которые зависят от страницы и зависят от доступности API базы данных Drupal. По сути, я хотел бы выполнить эти задачи после того, как ответ страницы будет полностью возвращен в браузер. Я всегда просто предполагал, что hook_exit() был правильным выбором для такого рода вещей, но, посмотрев на его вызов в drupal_page_footer() в некоторых подробно, я не совсем уверен. Вот примечательный фрагмент из drupal_page_footer():

module_invoke_all('exit');

// Commit the user session, if needed.
drupal_session_commit();

if (variable_get('cache', 0) && ($cache = drupal_page_set_cache())) {
drupal_serve_page_from_cache($cache);
}
else {
  ob_flush();
}

Таким образом, похоже, что как кэшированные, так и некэшированные ответы не отправляются в браузер до тех пор, пока не будут запущены все реализации hook_exit() (очистка буфера и выполнение drupal_serve_page_from_cache() оба происходят после module_invoke_all('exit')). Это кажется немного противоречащим документации hook_exit(), в которой говорится, что "Этот крючок НЕ ДОЛЖЕН ничего печатать, потому что к моменту его запуска ответ уже отправлен на браузер".

Возможно, я неправильно истолковываю то, как реализации hook_exit() работают в отношении доставки ответа? Если нет, существует ли какой-либо альтернативный способ запуска пользовательской логики ведения журнала позже в ответе (пока доступ к БД все еще доступен)? Возможно, зарегистрировав функцию выключения или что-то в этом роде?

Author: kiamlaluno, 2015-02-05

2 answers

Вы ищете fastcgi_finish_request(). Рекомендуем использовать его внутри функции выключения. Если вам нужно что-то еще, модуль HTTPRL должен быть в состоянии делать то, что вам нужно, с потоковой передачей.

 1
Author: mikeytown2, 2015-02-05 23:47:48

Я хочу продолжить это с ответом на мой собственный вопрос. Это просто расширяет уже принятый ответ.

Для тех, кто хочет достичь этого в многоразовом модуле, который может быть реализован в нескольких средах, простой вызов fastcgi_finish_request(), конечно, будет невозможен, поскольку нет гарантии, что используется FastCGI. Тем не менее, все равно похоже, что fastcgi_finish_request() является лучшим вариантом для этого, когда он доступен.

По-прежнему использовать некоторые преимущества, когда FastCGI не доступный, я обнаружил, что полезно, по крайней мере, попытаться сбросить все содержимое клиенту перед запуском задачи ведения журнала. Это позволит браузеру начать отображать контент, начать извлекать любые js, блокирующие рендеринг, и т. Д. До того, как произойдет регистрация. Это не позволит закрыть соединение, как это сделал бы fastcgi_finish_request() (так что события DOM ready все равно будут задерживаться), но все равно кажется лучше, чем вообще ничего не делать.

Вот что я тестировал в своем завершении работы функция (это было бы применимо только в функции выключения и не работало бы ни в каких реализациях подключений):

/**
 * Shutdown function used for logging purposes.
 */
function mymodule_shutdown() {
  // Try to ensure that our logging happens after the page content has been sent
  // to the browser, and ideally after the connection is closed.
  // fastcgi_finish_request() is our best option as it's designed for exactly
  // this purpose, but php may not always be running under FastCGI. As an
  // alternative we just force buffers to be flushed (this will not close the
  // connection but it will at least get the page content pushed out).
  if (function_exists('fastcgi_finish_request')) {
    fastcgi_finish_request();
  }
  else {
    flush();
  }
  run_expensive_logging_task();
}

В этом посте также есть некоторая полезная информация обо всем этом. Я не могу быть слишком уверен, что вызов flush() в одиночку всегда будет выполнять эту работу, поскольку есть некоторые признаки того, что буферизация вывода также должна быть явно отключена (ob_end_flush()). Однако в контексте функции завершения работы Drupal кажется, что это не нужно, так как завершается вся буферизация вывода вероятно, уже был обработан ядром. Конечно, если используется fastcgi_finish_request(), он позаботится обо всем этом в одной строке.

 0
Author: rjacobs, 2017-05-23 11:33:47