W ostatnim okresie czasu stworzyłem sobie za cel zapoznania się z narzędziem Blackfire.io. Udało mi się je w końcu rozgryźć, i tym doświadczeniem chciałbym się z Wami podzielić. Opiszę najpierw konfigurację środowiska deweloperskiego, a następnie jak rozpocząć z tym pracę.
Blackfire.io zostało stworzone z myślą tak zwanego "profilowania" skryptów i aplikacji internetowych napisanych w języku PHP. To znaczy, daje możliwość pozyskania danych na temat: jak długo podany kod się wykonuje, ile pobiera wszelkiego typu zasobów oraz jakie jego części w którym momencie i jak często się wywołują. Dzięki tym programista jest w stanie określić czy w jego aplikacji nie zachodzą jakieś niepożądane sytuacje, na przykład, zwalniające jakikolwiek proces. Jest to więc idealne narzędzie do optymalizacji w momencie, gdy żadnego innego tego typu nie mamy pod ręką (jak np. we frameworku Symfony).
Przechodząc jednak do rzeczy. Aby zacząć naszą nową przygodę, wymagane jest założenie konta na http://blackfire.io. Po dokonaniu tej czynności i poprawnym zalogowaniu, przejdźmy na podstronę "My Account" => "Settings" => "Credentials". Znajdziemy tam dane autoryzacyjne potrzebne podczas integracji z Blackfire. Mowa tutaj o:
Do poprawnego funkcjonowania Blackfire.io na swoim serwerze potrzebujemy kilku rzeczy:
Wszystko powyższe może zostać zainstalowane na maszynie poprzez następującą komendę:
wget -O - https://packagecloud.io/gpg.key | apt-key add - \ && echo "deb http://packages.blackfire.io/debian any main" | tee /etc/apt/sources.list.d/blackfire.list \ && apt-get update \ && apt-get install blackfire-agent \ && version=$(php -r "echo PHP_MAJOR_VERSION.PHP_MINOR_VERSION;") \ && curl -A "Docker" -o /tmp/blackfire-probe.tar.gz -D - -L -s https://blackfire.io/api/v1/releases/probe/php/linux/amd64/$version \ && tar zxpf /tmp/blackfire-probe.tar.gz -C /tmp \ && mv /tmp/blackfire-*.so $(php -r "echo ini_get('extension_dir');")/blackfire.so \ && printf "extension=blackfire.so\nblackfire.agent_socket=tcp://127.0.0.1:8707\n" > /etc/php/5.6/cli/conf.d/blackfire.ini \ && printf "extension=blackfire.so\nblackfire.agent_socket=tcp://127.0.0.1:8707\n" > /etc/php/5.6/fpm/conf.d/blackfire.ini
Uwaga!
Bardzo ważne w powyższym przykładzie jest odpowiednie ustawienie URL do socketa. W tym przypadku będzie to tcp://127.0.0.1:8707
.
Kolejnym krokiem będzie ustawienie zmiennych środowiskowych, z których Blackfire będzie pobierać potrzebne mu dane konfiguracyjne.
BLACKFIRE_LOG_LEVEL="2" BLACKFIRE_LOG_FILE="stderr" BLACKFIRE_SOCKET="tcp://127.0.0.1:8707" BLACKFIRE_CLIENT_ID="x" BLACKFIRE_CLIENT_TOKEN="y" BLACKFIRE_SERVER_ID="z" BLACKFIRE_SERVER_TOKEN="q"
Oczywiście, tak dla przypomnienia, ustawianie ich w linuksie dokonujemy poprzez:
export ZMIENNA="wartość"
Ostatnim krokiem jest włączenie agenta od Blackfire. Wystarczy więc wywołać komendę blackfire agent
, a naszym oczom powinien ukazać się następujący komunikat:
Agent listening on 127.0.0.1:8707
Swoją praktykę i eksperymenty z Blackfire opieram o kontener Dockera:
https://github.com/ktrzos/docker-blackfire
W powyższym repozytorium znajduje się pełna konfiguracja wraz z prostą aplikacją do profilowania pod adresem http://l.domain (nie zapomnijcie dodać domeny do /etc/hosts
).
Aby uruchomić serwer, należy najpierw skopiować plik .env.dist
jako .env
i uzupełnić odpowiednie dane wspomniane w dziale "Konto blackfire.io". Następnie, z poziomu głównego katalogu repozytorium, wykonać komendę docker-compose up --build
. Spowoduje to najpierw zbudowanie kontenera, a później jego uruchomienie. W czasie tej drugiej czynności, automatycznie włączany jest agent od Blackfire.io (dzięki pomocy supervisord).
Mając już gotowy serwer, czas przejść do praktyki. Profilowania poprzez Blackfire.io można dokonać na dwa sposoby:
Pierwszym wspomnianym sposobem jest analiza aplikacji przez konsolę poprzez skrypt blackfire
:
blackfire curl http://domain.com
blackfire run path/to/file.php
Wynikiem powinien być następujący komunikat w konsoli:
Profiling: [########################################] 10/10 Blackfire cURL completed Graph URL https://blackfire.io/profiles/XXX/graph No tests! Create some now https://blackfire.io/docs/cookbooks/tests No recommendations Wall Time 258µs I/O Wait n/a CPU Time n/a Memory 1KB Network n/a n/a n/a SQL n/a n/a
Powyższy sposób jest najlepszy do profilowania programów niedostępnych z poziomu protokołu HTTP.
Do Blackfire.io istnieje dość spora ilość integracji. W tym momencie interesują nas dwie z nich, czyli umożliwiających profilowanie poprzez przeglądarki:
Po zainstalowaniu odpowiedniej wtyczki, użytkownicy otrzymują dostęp do nowego okienka pop-up. W momencie kliknięcia w przycisk "Profile!", zostaje dokonany profiling aplikacji z aktualnie aktywnej zakładki. Często więc ten sposób analizy stron internetowych będzie o wiele wygodniejszy.
Taki pop-up ukaże się w momencie kliknięcia w przycisk wtyczki od Blackfire.
Pasek narzędzi pokazujący się po dokonaniu profilowania.
Każdy z wcześniej wspomnianych sposobów profilowania daje możliwość przejścia do odpowiedniego miejsca prezentującego wyniki. Domyślnym takim endpointem, możliwym w przyłości do zmiany na inny (np. zaimplementowany przez siebie), jest http://blackfire.io. Dlatego też to tutaj dane zostaną odpowiednio przeanalizowane i zaprezentowane.
W związku z czym, po profilowaniu użytkownik otrzyma adres URL, pod którym dostępna będzie wizualizacja wszystkich danych. Jeden z nich został już zaprezentowany w dziale "Profilowanie z poziomu konsoli" (XXX
to unikalny ciąg znaków reprezentujący jeden zestaw statystyk):
https://blackfire.io/profiles/XXX/graph
Po wejściu na ten adres, ukaże się wynik profilowania podzielony na trzy sekcje:
Główna strona pojedynczego wyniku profilowania.
Toolbar wyświetla podsumowane informacje związane z dokonanym profilowaniem. Można w nim znaleźć, między innymi:
Jest też swojego rodzaju elementem nawigacyjnym. Zawiera odnośniki kierujące do odpowiednich działów z bardziej szczegółowymi danymi i statystykami.
Sekcja przedstawiająca diagram wykonanego (podczas profilowania) kodu. Każdy węzeł jest reprezentacją jednej funkcji lub metody, i każdy z nich może mieć mniej lub bardziej kolorowe tło bądź brzegi. Intensywność koloru oznacza poziom ich aktywności, którego istnieją dwa rodzaje:
Przykład
Weźmy za przykład następującą klasę z dwiema metodami:
class Articles { // ... public function getArticleTitle(int $id) { $article = $this->loadArticle($id); return $article->title; } private function loadArticle(int $id) { $article = $this->db->load('Article', $id); } // ... }
Załóżmy, że wykonanie funkcji getArticle()
pochłonęło 10ms, a funkcji loadArticle()
około 30ms. Oznacza to więc, że "exclusive time" dla tej pierwszej będzie wynosić 10ms, a "inclusive time" 40ms.
Sekcja z diagramem raportu z profilowania.
W diagramie te pierwsze aktywności wskazywane są poprzez grubsze obramowania. Drugie natomiast mają odpowiednio intensywniejsze kolory tła. Dodatkowo, węzły inclusive są węzłami należącymi do tak zwanych ścieżek "Hot Path" będących najbardziej aktywnymi wewnątrz aplikacji. W związku z czym, powinny być pierwszym miejscem do analizy pod względem optymalizacji aplikacji.
Panel boczny z lewej strony jest równie ważny co wspomniane wcześniej sekcje. Spośród trzech dostępnych tam zakładek, skupię się na "Functions". Jak w przypadku diagramu, wyświetlana jest tu lista wszystkich funkcji oraz metod, których aktywność z wybranego działu wynosi co najmniej 1%. Dzięki niej użytkownik jest w stanie porównać które z nich są najbardziej czasochłonne lub pobierają najwięcej zasobów, co znacząco ułatwi znalezienie ewentualnie niepożądanych zachowań.
Notka
Jak zostało już wspomniane, w diagramie i zakładce "Functions" pokazywane są elementy, które stanowią więcej niż 1% całościowego wykonania się skryptu PHP. W związku z czym, niektóre partie kodu są w pierwszej kolejności ukryte. Powodem jest zwiększenie czytelności obu sekcji.
Zakładka z listą funkcji oraz metod.
Każdy rekord tej listy zawiera następujący zestaw informacji:
Jest to oczywiście tylko podstawowy zestaw informacji. Można dowiedzieć się o wiele więcej klikając na odpowiednią pozycję:
Szczegóły pojedynczej pozycji z listy funkcji oraz metod.
Więcej informacji: Analyzing Call Graphs.
Przedstawiłem Wam podstawowy zakres narzędzia, jakim jest Blackfire.io. Ominąłem parę sekcji, aby artykuł nie był zbyt długi, co mam nadzieję, nie weźmiecie mi za złe. Jak widzicie, ilość prezentowanych informacji jest imponująca. Może zatem znacząco ułatwić pracę przy optymalizacji aplikacji jeśli nie ma się pod ręką profilera dostarczanego przez framework (jak np. w Symfony lub Laravel). Plusem jest możliwość korzystania z niego za darmo, ale niestety w obrębie aplikacji postawionych lokalnie, i z ograniczoną ilością uzyskiwanych informacji. Do podstawowej analizy powinno to każdemu wystarczyć. Jeśli jednak nie, cennik wygląda następująco (stan z dnia 2 maja 2018 r.):
Jeśli zainteresowałem Was tematem, proponuję zapoznać się z dokumentacją. Znajdziecie tam parę przydatnych poradników oraz dokładniejszych opisów poszczególnych funkcjonalności. Zapraszam również do komentowania i wytykania mi błędów, jeśli takowe istnieją :). Ponadto, podzielcie się oczywiście swoimi doświadczeniami z Blackfire.
Niektóre z obrazów zaczerpnąłem ze strony http://blackfire.io.
Dodaj komentarz