Я отслеживаю этот пост, потому что, похоже, ни один из ответов не решил проблему. У меня была похожая проблема в D7 (и mod_fcgi), и поэтому я нашел этот поток, эффект не ограничен Drupal 6, служба временного хранения в Drupal 8 может облегчить это.
Короче говоря, я бы рассматривал состояние расы как основную причину. Веб-сайты, созданные с использованием Drupal и других фреймворков, использующих Ajax, могут стать сложными зверями с одновременным доступом к переменным сеанса. Сначала я бы посмотрел на длительные запросы, используя, например, Firefox-Developer Edition, анализатор трафика и т. д. Такие могут быть представлены, например, в блоках или другой структуре на сайте, любой из которых может получить доступ к $ _SESSION. Затем отключите как можно больше блоков и меню, пока проблема не исчезнет.
Кроме того, вы можете добавить отладочную информацию к includes/session.inc
в функциях drupal_session_start()
и drupal_session_commit()
. Выходные данные должны содержать идентификаторы процессов сервера. Ищите коммиты, которые происходят спустя много времени после отображения основного контента.
Гипотеза о том, что может происходить
Длительный запрос перезаписывает переменные сеанса со старым состоянием. Отчасти это связано с тем, как переменные сеанса управляются в Drupal. По умолчанию данные сеанса хранятся в столбце сеанса таблицы сеансов в виде единого сериализованного строкового представления данных. В принципе, чтение и запись переменных сеанса должно быть атомарным действием, но это не так легко осуществить. Код ниже взят из файла session.inc и обрабатывает запись данных сеанса. Он использует метку времени и флаг is_changed для эффективной задержки записи. Можно также сказать, что сессии не являются потокобезопасными. Кроме того, он использует операторы SQL MERGE, но не может объединять сериализованные переменные; по сути, последнее написанное состояние - это состояние победы.
// For performance reasons, do not update the sessions table, unless
// $_SESSION has changed or more than 180 has passed since the last update.
if ($is_changed || !isset($user->timestamp) || REQUEST_TIME - $user->timestamp > variable_get('session_write_interval', 180)) {
// Either ssid or sid or both will be added from $key below.
$fields = array(
'uid' => $user->uid,
'cache' => isset($user->cache) ? $user->cache : 0,
'hostname' => ip_address(),
'session' => $value,
'timestamp' => REQUEST_TIME,
);
...
db_merge('sessions')
->key($key)
->fields($fields)
->execute();
Представьте себе следующий сценарий:
Основным содержанием сайта является модуль, который отображает кнопку и счетчик. Счетчик сохраняется в $ _SESSION ['counter'] и увеличивается с помощью нажатия кнопки. Кнопка имеет обратный вызов Ajax, который эффективно выполняет следующее
function counter_increment(){
if (!drupal_session_started()) {
// Must initialize sessions for anonymous users.
drupal_session_start();
}
$_SESSION['counter'] = (isset($_SESSION['counter'])) ? $_SESSION['counter']++ : 0;
drupal_session_commit();
...
}
В блоке нижнего колонтитула сайта есть блок загрузки данных сеанса, который по какой-либо причине останавливается.
function do_nothing(){
if (!drupal_session_started()) {
// Must initialize sessions for anonymous users.
drupal_session_start();
}
$otherdata = $_SESSION['otherdata'];
// do something that takes a long time and eventually is finished or fails, like starting a site search:
wait(200); // might work, request is now older than write delay (default 180)
$_SESSION['otherdata'] = $otherdata; // session is updated now, too, possibly not necessary
return TRUE;
}
Таким образом, как только содержимое загружено, блок нижнего колонтитула вызывает do_nothing, которое зависает в течение> 180 секунд. Тем временем пользователь увеличивает счетчик с помощью обратного вызова, но после завершения do_nothing все содержимое сеанса будет заменено содержимым сеанса запроса do_nothing, что приведет к потере $ _SESSION ['counter'];