Как стать автором
Обновить

Комментарии 48

Нет, я всё понимаю, отраслевое арго, все дела… Типа «у бульдозера клапанА, у оргАна клАпаны».

Но, тем не менее…

локировки блокировки
лочит блокирует
Извините, но некогда заниматься лингвистическими мастурбациями :-) Хочется поделиться полезной информацией с коллегами — они поймут из конктеста.
Это Васе Пупкину на ЛОРе можно хоть лочить, хоть мастурбировать. А в корпоративном блоге приличной компании «лочить» == «чавкать на пресс-конференции» :)

Я использую устоявшийся технологический сленг, понятный профессиональному сообществу. Может переписать все в стихах и завизировать тексты у вас? :-)
[irony]Можно стать еще более понятным профессиональному сообществу, если использовать слова «пых», «ось», «таск» и т.д. вместо «лингво-мастурбационных» PHP, операционная система, задача...[/irony]
Давайте говорить не дистрибуция, а распределение, запретим говорить слова дистрибутив и форк, а любой автор на хабре должен сидеть перед словарем иностранных слов неделю чтобы его работу оценили филологи, подрабатывающие разработчиками :-)
По просьбе преподавателей русского языка и программистов с филологическим образованием заменил слово «локировка» на «блокировка».
Не заменил следующие слова, гореть мне в аду теперь:
аякс
таб
кастомный
засыпающий файл
файлик

:-)
Откройте для себя хранение сессий в MemCache/Redis и будут ваши «локеры» нежными и шелковистыми. И в русском есть достаточно устоявшаяся терминология на тему блокировок.
Открыл и что, там нет локировок? :-) Конечно есть, и больше. Тут в архитектуре и голове дело — что лочить и когда.
Есть, но в отличии от файловых операций они требуют меньшего времени и поэтому процессы друг другу мешают в гораздо меньшей степени.
Меньшего времени?? Вы хотите сказать что системный вызов к закэшированному файловому дескриптору в памяти медленнее обращения по TCP/IP к memcached или еще более тяжелому слону Redis? Шутите? :-)
Нисколько не шучу.
Суммарное время при работе через localhost на блокировку-чтение-освобождение блокировки при работе с Редисом значительно меньше, чем при тех же самых операциях, но с сессиями в файле, коих в директории может быть пара-сотня тысяч.
Без цифр сложно говорить. Пара тысяч файлов сессий не может тормозить, а когда их сотня тысяч — тут уже проблема с архитектурой, локальное хранилище может оказаться быстрее.

Но проблема то, согласитесь, остается — работа с сессией на веб-странице, при которой сессия лочится (сорри, уважаемые лингвисты) блокируется на все время работы скрипта (блин, можно ли писать слово скрипт? может нужно «текстовый файл, содержащий команды интерпретатора PHP»). Статья именно о проблеме работы с сайтом в рамках одной сессии — неужели никто не сталкивался?
Также хочу отметить, что время ожидания локировки блокировки на несколько порядков выше самого выполнения вызова что к файлу что через локальный сокет к хранилищу типа memcached, redis, mysql. И чем это поможет, если маленький аякс занял сессию на 10 секунд и остальные элементы интерфейса ждут?
Не понял. А чем будет отличаться блокировка?

<?php

session_start(); //блокировка
sleep(30);
die; //разблокировка

Следующий скрипт сможет выполнить session_start() не раньше, чем через 30 сек.
Даже если переопределить обработчики функций для работы с сессиями? Хмм, этот вопрос требует практической проверки — набросаю тест и посмотрим.
Так обработчик часто в проектах переопределяют и хранят сессии и в БД, и в memcached. Проблема паровозика никуда не уходит — один хит, захватывающий сессию эксклюзивно на 20 секунд и половина воркеров веб-сервера моментально забиваются. Тут нужно в каждом хите либо лочить разные куски объекта сессии :-), либо лочить только для чтения — ту тогда придется переделывать логику приложения…
Просто, все кто плюсует Greendq, скорее всего, забыли реализовать блокировки в своем обработчике сессий. А без блокировок оно конечно быстро будет работать.
Если сессию хранить как допустим строку в таблице БД, ее же нужно сначала открыть, затем залочить либо в SHRED (все читают, один пишет), либо в EXCLUSIVE LOCK (один читает и пишет, все ждут), затем записать измененные данные, затем закрыть. Если все будут писать без локировок — случиться каша, согласен.

Т.е. проблема даже не PHP и сессий, а глубже — разделение доступа к объекту, семафорами и мьютексами пахнет :-)
"все читают, один пишет" — будут ждать не на стадии открытия, а на стадии записи, плюс будут читать устаревшие данные (т.к. они еще не записаны).

"один читает и пишет, все ждут" — так работает механизм по-умолчанию.

Самым распространенным решением, все же, является написание своего обработчика, который работает без блокировок. Таким образом, мы получаем проблемы атомарной работы с данными, но выигрываем скорость. Отсюда имеем:
  • проблемы будут ограничиваться данными одного пользователя (чья сессия)
  • их вероятность довольна низка (одновременное открытие многих табов, шустрые обильные аякс-запросы)
  • цена ошибки, чаще всего, невелика (счетчик пропустит значение)

Так и живем.

Есть еще подход с куками, упомянутый Maxkn, я о нем задумывался, но на практике не реализовывал, хотя особенно подкупает «отвязка» от сервера.
Если Вам интересен подход с шифрованными/подписанными куками, то помедитировать можно над реализацией в фреймворке Slim и фреймворке MicroMVC. Это микрофреймворки, поэтому с кодом там все предельно просто.

P.S. Пользователи фреймворка Slim с опаской относятся к такому механизму хранения данных.
Всё, теперь понятно. Блокировки надо реализовывать самостоятельно (кстати — в документации PHP об этом вообще не упоминается). Но и без блокировок можно работать, если весь код будет следовать чётким правилам по записи и чтению данных из сессии.
А как следовать этим четким правилам если процессы выполняются параллельно? Нужны примитивы синхронизации, здравствуйте мьютексы и дедушка Дейкстра.
Ну есть же варианты логики, где читатели не блокируют писателей :) Но синхронизация в случае записи всё равно нужна, согласен.
То есть, ядро PHP внутри самостоятельно использует блокировки для всех переопределяемых функций работы с сессиями, вне зависомости от того, что в самой функции? Странно, я получал совсем другую картину при использовании редиса, надо бы посмотреть исходники PHP, чтобы быть уверенным наверняка, но чего-то меня они совсем не привлекают…
Идея в том, что один скрипт захватывает сессию на 30 секунд, а остальные просто ее пытаются открыть и не собираются спать по 30 секунд — они не смогут этого сделать, А если бы засыпали на 30 секунд, все бы выстроились паровозиком друг за другом — что ударит и по производительности и по загруженности воркеров веб-сервера.
Вас я понял. Ситуация такая:

<?php
//script1.php
session_start(); //блокировка
sleep(30);
die;


<?php
//script2.php
session_start(); //ждем


Все запущенные копии script2.php будут ждать. Верно?
верно. Можно die убрать, он не влияет на логику. Все скриты хотят взять объект сессии эксклюзивно для записи в него, вот и ждут друг друга.

Читаю заголовок: «Локировки сессий в веб-проектах...» И в сознании откладывается, что речь пойдет о «лакировках», и возникает легкое удивление — а сессии можно еще и «лакировать»? И только потом понимаю, что на самом деле речь о «блокировках»

Конец заголовка "… выбираем эффективное оружие" вызывает ожидание, что будет дан некий совет/рецепт/методика о том, как избежать длительных блокировок сессии. Однако, встречаю лишь совет «отстреливать процессы веб-сервера».

В общем, упс, как говорят американцы. И, в отличие от автора, не разделяю надежды, что «подобные практические статьи будут полезны… и веб-разработчикам» (акцент на последнюю фразу)
Уговорили, поправлю. Не ожидал бывших студентов филологов и преподавателей русского языка среди программистов и сисадминов :-)

Нет уж, я дал в статье инструмент выявления проблемы и пару способов борьбы. Достаточно, чтобы пойти к программистам с пивом :-)
Если ожидали увидеть исключительно тех, кто русский язык учит по англоязычной документации, то явно ошиблись. :)

Про пиво в статье тоже видел, но не подумал, что это тоже один из способов борьбы.

А если серьезно: понимаю, что проблема имеет место быть, и согласен, что далеко не всегда корни проблем ищутся, где нужно (поэтому статья, несмотря на придирки, весьма полезна), но в статье вижу методику изучения проблемы, вижу методику сбора статистики, а пару способов борьбы с ней — не вижу. Или пара методов — это «отстрел» и «рефакторинг»? Тогда первый — явно из категории «зловредных советов», а второй — слишком уж общий, типа «надо хорошо учиться и писать правильный код»
Спасибо за вопрос. Если серьезно, то вижу целесообразным придерживаться при разработке следующих правил:

1) Захватывать сессию управляемо — не на все время выполнения скрипта (как работает по умолчанию), а тогда, когда это действительно нужно.
2) Не выполнять на хитах долгие операции, способные заблокировать сессию клиента — а быстро оформлять их как задачу (job) например в gearman (или проще через БД) и выполнять в фоне за пределами сессии. Затем браузер периодически проверяет готовность задачи и обновляет часть интерфейса.
3) Постараться вынести части веб-сайта в отдельные компоненты, собираемые не в рамках сессии, например через nginx ssi — тогда в сессии нужно будет выполнить минимум бизнес-операций.

И т.д. Но общий смысл рекомендаций — не полагаться на режим работы с сессией по умолчанию, а проявить армейскую смекалку :-)
Хабр стал площадкой для лингвистических баталий?
Я сам в шоке, откуда они появились. Хоть бы один задал вопрос по логике кода, методике анализа проблемы. Один троллинг пока :-)
Не проще, положить все что надо в куку, куку подписать и отдать пользователю и забыть про весь описанный геморрой? ну если вы конечно не храните в сессии пару мегабайт
Так сессию ж специально придумали, в частности, чтобы не гонять информацию туда-сюда через куки. В куках только ID сессии лежит.
Когда сессию придумывали были несколько иные окружающие условия. Сейчас же вы ради хранения 1-2к информации о пользователе придумали адский костыль, который еще и не будет работать если веб серверов станет больше одного. в попретесь в сторону редиса, потом будете долго реплицировать, потом шардить и реплицировать. потом на вас обрушитьс еще пара напастей. зачем?
Соглашусь, что в некоторых довольно простых кейсах можно рассмотреть вопрос хранения только кук. Но ~4кБ для приложений со сложной бизнес-логикой станет потолком в объеме данных сессии, придется еще где-то хранить.
гкхм, извините, а что вы в сессии храните?
Это не решает проблему гонок. Вот представьте, есть у вас скрипт, который должен увеличивать значение в «сессии» на единицу при каждом обращении. В случае с куками и асинхронными запросами со стороны клиента может получиться такая вот ситуация:

когда, несмотря на то, что запросов к серверу было 2, в итоге значение было увеличено лишь на единицу.
на мой взгляд, это слегка странное желание что-то увеличивать в сессии при каждом запросе. как только у вас в проекте фронтенд серверов станет больше 1 и разные запросы одного пользователя начнут выполнятся на разных серверах — сразу поплохеет. потом захотите патчить продакшен без потери текущих сессий — опять проблемы.

сессии были придуманы очень давно. когда были иные задачи, иные возможности для их реализации и иные нагрузки. Они плохо масштабируются, висят мертвым грузом на сервере, пока пользователь наливает себе кофе и порождают непонятные пляски с бубнами типа описанных в топике. Если начинаются пляски вокруг сессий в почти 100% случаев это говорит о неправильной организации данных.

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

Замените «увеличить» на «добавить товар в корзину», если вам так будет проще представить суть проблемы. А если у вас нет вычислений, которые, основываются на данных из сессии и результат пишут туда же, то и с серверной стороны вам блокировки не нужны.
Что-то не уловил о каких блокировках идет речь. Очевидно sessionID разные для разных сессий, и выстраиваться в одну очередь будут скрипты лишь которые получили один sessionID из запроса. Т.е. 100500 аяксов одного пользователя может и выстроятся в очередь — так проблема в архитектуре бизнес-процессов а не в блокировках. В остальном ничего такого супер-страшного чтобы панику наводить. Хочется использовать воркеры — так уж можно и позаботиться о shared_memory и семафорах для разделяемых сессий. Вот автор, вы этим и займитесь, но за примеры мониторинга тоже спасибо!
А ведь на дворе уже 2013й год

Почему ни одного упоминания про Web storage?
Уважаемому автору не встречались случаи зависания php-cgi в функции poll_schedule_timeout? Загрузка ЦПУ при этом падает до 0, и лечится только перезапуском php-cgi.
Можно попытаться войти в процесс через gdb и посмотреть чем он занимается в этот момент. strace — не покажет деталей тут.
strace показывает в цикле:

poll([{fd=8, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 0 (Timeout)
poll([{fd=8, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 1000) = 0 (Timeout)
войти в процесс через gdb
Зарегистрируйтесь на Хабре, чтобы оставить комментарий