Как выполнить задачи, связанные с запросом, после возврата ответа страницы. Действительно ли 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() работают в отношении доставки ответа? Если нет, существует ли какой-либо альтернативный способ запуска пользовательской логики ведения журнала позже в ответе (пока доступ к БД все еще доступен)? Возможно, зарегистрировав функцию выключения или что-то в этом роде?
2 answers
Вы ищете fastcgi_finish_request(). Рекомендуем использовать его внутри функции выключения. Если вам нужно что-то еще, модуль HTTPRL должен быть в состоянии делать то, что вам нужно, с потоковой передачей.
Я хочу продолжить это с ответом на мой собственный вопрос. Это просто расширяет уже принятый ответ.
Для тех, кто хочет достичь этого в многоразовом модуле, который может быть реализован в нескольких средах, простой вызов 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()
, он позаботится обо всем этом в одной строке.