Ne v kontakte Antisocial programmer's blog

Железобетонные мьютексы в PHP

Железобетонные мьютексы в PHP

    dev     php     mutex     concurrency     articles

Я хочу рассказать об одном нестандартном применении механизма сессий в PHP. Вспомнилось мне это в связи с позавчерашним постом Тормоза на тему, что опять в Даосе проблема с параллельным доступом к файлам - функция блокировки дала очередной сбой. И хотя Тормоз в комментах уже писал, что обкатывает исправленный алгоритм, успешно выдерживающий стресс-тест, я все же поделюсь своим решением. Сразу оговорюсь, все нижеизложенное было проделано just for fun и имеет свои недостатки. Зато и работает практически безотказно.

Перейду к технической части. По умолчанию механизм сессий в PHP использует хранилище данных в файлах - каждая сессия лежит в своем файле. При этом, чтобы обеспечить целостность данных используется нехитрый подход эксклюзивной блокировки доступа к файлу. Иными словами, в момент, когда мы делаем session_start(), движок PHP захватывает файл с требуемой сессией и отпускает ее только в момент явного вызова session_write_close() или завершения скрипта. И если в то время, пока мы работаем с сессией в рамках одного запроса, произойдет еще один запрос, использующий ту же сессию, то он заблокируется на session_start() до тех пор, пока первый не освободит файл. Обычно разные клиенты работают в разных сессиях и проблемы не возникает, однако (немного огрубляю) у одного и того же клиента в один момент времени исполняется только один запрос.

Реально проблема встает, когда во время выполнения некоторого длительного запроса (стримминг потока или long polling) надо обеспечить клиенту возможность посылать и другие запросы (ходить по страничкам) и вовремя получать ответы. Решают задаю обычно одним из трех путей:

  1. Реализуют полностью свой механизм сессий.
  2. Переопределяют обработчики операций с данными сессии во встроенном механизме (с помощью session_set_save_handler(), этим вариантом я в свое время и воспользовался).
  3. Как можно раньше освобождают сессию с помощью session_write_close().

Read more →