Ogólnie przyjętym wzorcem stosowanym w różnego rodzaju aplikacjach jest logowanie zdarzeń, np. wystąpienie błędu danego typu. Następnie na podstawie zalogowanych zdarzeń podejmowane są odpowiednie akcje, które będą wywoływane co ustalony interwał czasu bądź bezpośrednio po wystąpieniu zdarzenia. W pierwszym przypadku będziemy się posiłkowali CRONem, w drugim skorzystamy z wbudowanych (w OS) mechanizmów reagujących na wystąpienie zdarzenia. W systemach o dużej skali, gdzie logowanych zdarzeń może być sporo oraz zależy nam na efektywnym wykorzystaniu zasobów, skorzystamy z drugiego proponowanego tutaj rozwiązania - reagowanie na zdarzenia co ustalony interwał czasu może skutkować pustymi przebiegami. Przykładem takiej biblioteki, wspieranej przez PHP, może być inotify.
Inotify
Inotify jest mechanizmem, wbudowanym w kernel Linuxowy, bazującym na paradygmacie wszystko w systemach Linuxowych jest plikami - po prostu operuje na deskryptorach plików. Umożliwia monitorowanie zmian w systemie plików i reaguje natychmiast w momencie wystąpienia zdarzenia, np. utworzenia nowego pliku. Wykorzystanie tego frameworka, z poziomu API PHP, sprowadzania się do utworzenia instancji inotify oraz podpięcia watcher’ów monitorujących zmiany w danych katalogach.
Monitorowane zdarzenia
Wykorzystanie powyższego fragmentu kodu umożliwia reagowanie na dwa typy zdarzeń IN_CREATE (utworzenie nowego pliku) oraz IN_ATTRIB (zmiana atrybutów pliku, np. data ostatniej modyfikacji). Oczywiście, nasz monitoring nie będzie sprowadzał się wyłącznie do takich zdarzeń, dlatego też w razie potrzeby możemy dodać obsługę zdarzeń dowolnego typu. Pełną listę zdarzeń możemy znaleźć w dokumentacji.
Kolejnym krokiem, po utworzeniu instancji inotify oraz zdefiniowaniu monitorowych typów zdarzeń i katalogów, jest reagowanie na te zdarzenia. Każde zarejestrowane zdarzenie w systemie plików, zwracane przez inotify_read(), zawiera deskryptor podpiętego watcher’a oraz dopasowaną maskę (typ zdarzenia, np. IN_CREATE). Na podstawie tych atrybutów możemy reagować według potrzeb, np. zwiększając licznik utworzonych plików.
Buforowanie zdarzeń
Monitorowane zmiany w systemie plików są buforowane dzięki czemu możliwa jest obsługa wielu zdarzeń równocześnie. Każdy z podłączonych watcher’ów, a więc każdy ze śledzonych katalogów, posiada własny bufor. Jeśli zdarzeń, czyli zmian w systemie plików będzie bardzo dużo bądź nie zdążymy ich obsłużyć odpowiednio szybko, może wystąpić przepełnienie bufora. Sytuacja taka sygnalizowana jest poprzez maskę IN_Q_OVERFLOW zwracaną jako jeden z atrybutów zarejestrowanych zdarzeń.
Rozmiar bufora kontrolowany jest poprzez zmianę parametrów konfiguracyjnych inotify, do których możemy zaliczyć:
- /proc/sys/fs/inotify/max_queued_events - rozmiar bufora (osobny dla każdego z watcher’ów), jeśli zostanie przekroczony nowe zdarzenia nie będą przyjmowane ale każdorazowo zwracana będzie maska Q_IN_OVERFLOW (oraz wd = -1), domyślnie 16384
- /proc/sys/fs/inotify/max_user_instances - max liczba instancji inotify per użytkownik, domyślnie 128
- /proc/sys/fs/inotify/max_user_watches - max liczba watcher’ów per użytkownik, domyślnie 8192
Przedstawione powyżej rozwiązanie powala reagować na zdarzenia w aplikacji w momencie wystąpienia dzięki monitorowaniu zmian w systemie plików. Dzięki temu dostępne zasoby (CPU, pamięć, łącze itd.) wykorzystane będą efektywnie, a przy tym samo rozwiązanie jest bardzo mało obciążające dla systemu. Typy monitorowanych zdarzeń należy obsługiwać rozważnie, ponieważ jednej zmianie w systemie plików może towarzyszyć kilka zdarzeń, np. utworzeniu nowego pliku towarzyszą m.in zdarzenia typu IN_CREATE oraz IN_ATTRIB. Dodatkowo ograniczeni jesteśmy wyłącznie do systemów Linuxowych (i to nie wszystkich) oraz pojedynczego katalogu (dla poszczególnych podkatalogów trzeba zdefiniować odrębne watcher’y), ale za to proste API i małe wymagania systemowe pozwolą bardzo szybko zaadoptować to rozwiązanie w Waszych aplikacjach.
Kompletny kod omawianego w tym artykule rozwiązania dostępny jest tutaj.
Przydatne linki:
- http://man7.org/linux/man-pages/man7/inotify.7.html
- http://ph7spot.com/musings/in-unix-everything-is-a-file
- http://en.wikipedia.org/wiki/Everything_is_a_file
- http://www.ibm.com/developerworks/library/l-ubuntu-inotify/
- http://www.ibm.com/developerworks/library/l-inotify/
- http://www.linuxjournal.com/node/8478/print
- http://www.serverphorums.com/read.php?12,216856
- http://askubuntu.com/questions/154255/how-can-i-tell-if-i-am-out-of-inotify-watches
- https://github.com/griffbrad/php-pecl-fsevents
- http://www.opensourceforu.com/2011/04/getting-started-with-inotify/
- http://www.go4expert.com/articles/monitor-filesystem-changes-php-t29348/
- http://php.net/manual/en/book.inotify.php
- https://github.com/henrikbjorn/Lurker/blob/master/src/Lurker/Tracker/InotifyTracker.php
- https://github.com/mkraemer/react-inotify/blob/master/src/MKraemer/ReactInotify/Inotify.php
tagi: php , event-driven programming , events , file system , inotify