Autor wpisu: Zyx, dodany: 12.06.2011 13:06, tagi: php
Autor wpisu: Athlan, dodany: 12.06.2011 00:11, tagi: php
Praktyka cache‘owania danych jest powszechna wśród programistów aplikacji webowych ze względu na optymalizację dostępu do danych bezpośrednio ze źródła ich pochodzenia, a w szczególności:
- trudność dostępu (np. wykonanie skomplikowanych połączeń),
- ograniczenia dostępu (np. limit odpytywania),
- długi czas oczekiwania na dane; powodów jest wiele.
O ile tematyką stworzenia samego mechanizmu cache zajęli się m.in. Nospor, możecie podejrzeć jak to wygląda w Zend_Cache, Symfony, czy Kohana; tak ja chciałbym zwrócić uwagę na jeszcze jedną rzecz.
Zazwyczaj schemat kodu wygląda mniej więcej tak:
< ?php $oCache = new Cache(); // tworzony jest jakis obiekt cache if($oCache->expired(3600) || !is_array($aData = $oCache->load())) // sprawdzamy, czy jest cache i nie wygasł { $aData = $oModel->GetSomething(); // zbieramy dane z bazy danych $oCache->save($aData); } // $aData przechowuje nasze dane do użytku ?>
Symulacja, parę linijek kodu, a ile nieszczęść.
Wszystko działa pięknie, dopóki nie spotkamy się z sytuacją, gdy setki osób (procesów) jednocześnie zechcą zbierać takie dane z bazy danych. Przeprowadźmy zatem krótką dywagację. Załóżmy, że użytkownik #1 wchodzi na stronę, stwierdza, że nie ma cache, lub jest nieświeży, wówczas przechodzi do połączenia się z bazą danych i zaczyna zbierać dane. W tym samym czasie, zanim użytkownikowi #1 zostaną zwrócone dane wchodzi użytkownik #2, który stwierdza, że nie ma cache, bo użytkownik #1 jeszcze nie zebrał danych, postanawia połączyć się z bazą i zrobić to samo, co użytkownik #1, powtarzając niepotrzebnie czynność i dodatkowo obciążając bazę. Można by iść dalej i wprowadzić n użytkowników, którzy powtarzają czynność, dopóki dane nie pojawią się w cache i kolejni użytkownicy będą z niego korzystać. Co się stanie natomiast, gdy kolejka tak narośnie, że użytkownikowi #1 zabraknie zasobów systemowych, aby ukończyć proces zbierania danych, co spowoduje, że pozostałym też? Kolejka będzie wydłużała się w nieskończoność, póki system operacyjny nie podejmie żadnych działań (np. odłączy bazę danych, lub po prostu wyłączy serwer, np. w IIS7 wyłączy cały application pool). Aby doszło do tej kolizji nie jest potrzebne wcale natężenie użytkowników, serwer może akurat np. zajmować się wysyłką maili lub nieoptymalnie zrobionym procesem, który zajmuje zasoby, a w tym czasie wejdzie tylko pięciu użytkowników.
Parę linijek kodu, a ile nieszczęść.
Pojęcie semafora.
Semafor w informatyce – jest chronioną zmienną lub abstrakcyjnym typem danych, który stanowi klasyczną metodę kontroli dostępu przez wiele procesów do wspólnego zasobu w środowisku programowania równoległego.
Więcej na temat semaforów na Wikipedii, bądź w Podstawy informatyki / Stefan Węgrzyn. – Warszawa : Państwowe Wydawnictwo Naukowe, 1982.
Podejście do problemu.
< ?php $oCache = new Cache(); if($oCache->expired(3600) || !is_array($aData = $oCache->load())) { $oCache->savePrepare(); // stawiamy semafor $aData = $oModel->GetSomething(); $oCache->save($aData); // metoda save() może (nie musi) od razu zwolnić semafor, gdy próba zapisu się zakończy // jeżeli metoda save() nie zwalnia zasobu, możemy np. użyć: // $oCache->saveFinalize(); } // $aData przechowuje nasze dane do użytku ?>
Rozwiązaniem jest zastosowanie semafora blokującego dostęp do zasobu (w tym przypadku abstrakcyjnie “cache”, mniej abstrakcyjnie może być to plik na dysku, przestrzeń w pamięci operacyjnej, rekord w bazie danych, cokolwiek, co cache przerzymuje). Dla wartości semafora = 1 zasób jest wolny (nieużywany, jest 1 cache), gdy jest mniejszy/równy 0 zasób jest zajęty, ktoś z niego “korzysta”. Zajętość zasobu powinna być sprawdzana przy próbie odczytu. Dopóki zasób nie zostanie zwolniony, nie będzie można określić, czy są dane w cache. Jeżeli nie można określić, czy dane są w cache, należy zaczekać na zwolnienie zasobu.
Teraz nasze rozwiązanie nie dopuści do przytoczonej w powyższym przykładzie sytuacji. Zanim cache nie zostanie odblokowany po próbie zapisu, nie uzyskamy odczytu, czekając na niego i nie przechodząc w skrypcie nigdzie dalej.
Gdy save()
się nie powiedzie? Można zastosować timeouty odczytu na load()
. Wówczas złapalibyśmy wyjątek i przeszli dalej do realizacji zapisu, tak, jakby semafora nie było.
Autor wpisu: singles, dodany: 11.06.2011 12:43, tagi: zend_framework
Kolejny krótki wpis, także w ramach autolansu ;)
Dla tych co nie mają, bądź nie śledzą Twittera, to informuję, że od kilku miesięcy Getting Started With Zend Framework 1.11 autorstwa Rob Allena dostępny jest po polsku (w tłumaczeniu mojej skromnej osoby;). Znajdziecie go tutaj.
Wiem, że podstawowym językiem developera jest albo powinien być angielski, ale może wersja w ojczystym języku pomoże niektórym w zrozumieniu działania ZF.
Autor wpisu: singles, dodany: 11.06.2011 12:33, tagi: php, zend_framework
Kolejny wpis z serii Zend Framework Quick Tip
Podstawowym elementem sporej liczby aplikacji internetowych jest datagrid – tabela, której zawartość można filtrować, sortować, z pagerem. W ostatnio wykonywanej aplikacji natrafiliśmy na potrzebę wykorzystania tych samych datagridów w kilku miejsach.
Potrzeba
Do stworzenia gridów wykorzystaliśmy do tego celu bibliotekę ZFDataGrid (która wymagała kilkunastu poprawek, ale i tak nie znaleźliśmy niczego lepszego). Utworzyliśmy jedną bazową klasę Grid i po niej dziedziczyły wszystkie gridy zawarte w aplikacji, definiując własne źródła danych, filtry czy też akcje. Do pełni szczęścia brakowało, aby ładowane były na żądanie, wykorzystując domyślny Zendowy autoloader. Okazało się, że jest bardzo prosty sposób na osiągnięcie takiej funkcjonalności.
Implementacja
ZF domyślnie mapuje kilka rodzajów zasobów (resources) na ścieżkę w strukurze plików. Należą do nich:
Nam brakowało jeszcze prefixu Grid
, który mapowałby na ścieżkę grids
. Szybkie spojrzenie do manuala i mamy rozwiązanie. Wszystko ogranicziło się do dodania następującego kodu do Bootstrap.php
:
/** * Register 'Grid', so they could be created the same way like forms, models, etc. */ protected function _initGridsAutoload() { $this->getResourceLoader() ->addResourceType('grid', 'grids', 'Grid'); //or for many custom resources /* $this->getResourceLoader()->addResourceTypes(array( 'item' => array( 'path' => 'items/', 'namespace' => 'Item', ), 'task' => array( 'path' => 'some/deep/folder/tasks', 'namespace' => 'Task' ) )); */ }
Pierwszy parametr to wewnętrzny klucz dla loadera, drugi to ścieżka, pod jaką mają być szukane pliki – zaczynając od `application/’, a trzeci to prefix jakiego będziemy używać w aplikacji. I tyle :) Od tego momenu w można wykorzystywać konstrukcję:
$grid = new Grid_Users(); // autoload files application/grids/User.php
Podany przykład dla applicationNamespace = ” – w przypadku kiedy wykorzystujecie namespace powiedzmy My, albo App, będzie to oczywście `App_Grid_Users_.
Autor wpisu: batman, dodany: 11.06.2011 08:00, tagi: internet
Tytuł może być nieco mylący, ponieważ krótkie opisanie Bouce, wbrew pozorom nie jest łatwe. Czym jest Bounce? Jest to aplikacja online, która pozwala nanosić na wskazaną stronę internetową uwagi w postaci kolorowej ramki wraz z opisem w chmurce. Dzięki temu narzędziu w kilka chwil jesteśmy w stanie przesłać dowolne uwagi do dowolnej strony.
Jak to działa? Najpierw podajemy adres strony, którą chcemy opisać, następnie przy pomocy myszki zaznaczamy obszar, który chcemy opisać i na koniec wpisujemy nasz komentarz do chmurki. Chwilę później mamy gotowy link, który możemy wysłać autorowi strony. Jeśli z jakiegoś powodu nie możemy podać adresu strony, istnieje możliwość uploadowania obrazka, który chcemy w jakiś sposób opisać.
Aplikację znajdziecie pod adresem www.bounceapp.com.