Я хочу рассказать об одном нестандартном применении механизма сессий в PHP. Вспомнилось мне это в связи с позавчерашним постом Тормоза на тему, что опять в Даосе проблема с параллельным доступом к файлам - функция блокировки дала очередной сбой. И хотя Тормоз в комментах уже писал, что обкатывает исправленный алгоритм, успешно выдерживающий стресс-тест, я все же поделюсь своим решением. Сразу оговорюсь, все нижеизложенное было проделано just for fun и имеет свои недостатки. Зато и работает практически безотказно.
Перейду к технической части. По умолчанию механизм сессий в PHP использует хранилище данных в файлах - каждая сессия лежит в своем файле. При этом, чтобы обеспечить целостность данных используется нехитрый подход эксклюзивной блокировки доступа к файлу. Иными словами, в момент, когда мы делаем session_start(), движок PHP захватывает файл с требуемой сессией и отпускает ее только в момент явного вызова session_write_close() или завершения скрипта. И если в то время, пока мы работаем с сессией в рамках одного запроса, произойдет еще один запрос, использующий ту же сессию, то он заблокируется на session_start() до тех пор, пока первый не освободит файл. Обычно разные клиенты работают в разных сессиях и проблемы не возникает, однако (немного огрубляю) у одного и того же клиента в один момент времени исполняется только один запрос.
Реально проблема встает, когда во время выполнения некоторого длительного запроса (стримминг потока или long polling) надо обеспечить клиенту возможность посылать и другие запросы (ходить по страничкам) и вовремя получать ответы. Решают задаю обычно одним из трех путей:
Но я отвлекся. В нашем случае мы не только не будем избавляться от этого, но наоборот будем использовать для реализации мьютекса: будем создавать сессию с заранее известным session_id, к примеру db_mutex, выполнять критические действия и закрывать сессию. Именно эту функциональность и реализует мой класс PHP_Mutex.
Надо признать, такой подход имеет ряд недостатков по сравнению с полноценными мьютексами:
Но если все условия удовлетворены (кстати, класс по мере возможности сам отслеживает потенциально проблемные ситуации и выдает ворнинги), то блокировка будет железной - разработчики PHP постарались, чтобы данные сессии были в безопасности в любой среде окружения :) Поэтому класс вполне сгодится в небольших скриптах типа Даоса, где сессии не очень нужны. Ну а в сложных системах можно прибегнуть и к тяжелой артиллерии вроде встроенных семафоров, у которых лишь один недостаток - по умолчанию они отключены.
Сам класс вы найдете во вложении, распространяется он свободно согласно лицензии New BSD License. Если кому пригодится - на здоровье :-)
Veel kortings codes om te besparen bij alle nederlandse online winkels
Прикрепленный файл | Размер |
---|---|
mutex.php | 3.08 кб |
Прикольно, новое слово узнал - mutex :) Надо почитать.
Спасибо. Стресс-тесты провести с использованием твоей штуки интересно, конечно, но я уже боюсь за свой винт, честно говоря.
Откровенно говоря, мне делать стресс-тесты лень, но может если будет сильно нечем заняться ;-)
Или ты можешь написать тест, а я помучаю свой винт :-D
А тебе тогда придётся мучать винт не с одним тестом, исследования же сравнительные должны быть. На моей машине будут другие результаты.
Я понимаю. Но это не проблема.
Спасибо. Результаты будут более показательными, если прогнать аналогичные тесты сразу на двух машинах.
Хорошо, я постараюсь сделать функции с твоим вариантом и вышлю тебе тогда три дистрибутива Daos с разной реализацией + Bash скриптик для запуска. Но это завтра, не раньше. Сейчас уже в сон падаю, хотя ещё нужно подготовить дистр с вариантом от Jungle и запустить тест на ночь.
Погоди. Что-то или я не понял, или ты не понял задачу. А как эта штука будет себя вести, если файл запрашивают несколько РАЗНЫХ пользователей? Именно этот случай наиболее критичный.
Уфф, кажется понял, идентификатор ведь один. Ты зря у класса не описываешь интерейс, это было бы гораздо более полезно читать, чем BSD-лицензию :) Непростая штука, нетривиальная. Сейчас попробую применить.
У обоих публичных методов есть описание в стандартных блоках phpDoc - /** */
Хм. Почему-то у меня нельзя сессии писать:
Warning: session_start() [function.session-start]: open(/var/lib/php/sess_test, O_RDWR) failed: Permission denied (13) in /home/me/www/test/classMutex.php on line 74
PHP Warning: Unknown: open(/var/lib/php/sess_test, O_RDWR) failed: Permission denied (13) in Unknown on line 0
Warning: Unknown: open(/var/lib/php/sess_test, O_RDWR) failed: Permission denied (13) in Unknown on line 0
PHP Warning: Unknown: Failed to write session data (files). Please verify that the current setting of session.save_path is correct (/var/lib/php) in Unknown on line 0
Warning: Unknown: Failed to write session data (files). Please verify that the current setting of session.save_path is correct (/var/lib/php) in Unknown on line 0
Сейчас узнаю, как эту хрень включить.
Ага, session_save_path рулит ) Буду писать их в тот же каталог, где сами файлики.
А ты сам тестировал?
Notice: Use of undefined constant E_USER_FATAL - assumed 'E_USER_FATAL' in /home/me/www/test/classMutex.php on line 69
Warning: trigger_error() expects parameter 2 to be long, string given in /home/me/www/test/classMutex.php on line 69
Отправить комментарий