Pomiar wydajności za pomocą taktowania serwera
Opublikowany: 2022-03-10Podejmując jakąkolwiek pracę nad optymalizacją wydajności, jedną z pierwszych rzeczy, których się uczymy, jest to, że zanim będziesz mógł poprawić wydajność, musisz ją najpierw zmierzyć. Bez możliwości zmierzenia szybkości, z jaką coś działa, nie możemy stwierdzić, czy wprowadzane zmiany poprawiają wydajność, nie przynoszą żadnego efektu, a nawet pogarszają sytuację.
Wielu z nas na pewnym poziomie będzie zaznajomionych z pracą nad problemem wydajnościowym. Może to być coś tak prostego, jak próba ustalenia, dlaczego JavaScript na Twojej stronie nie uruchamia się wystarczająco szybko lub dlaczego wyświetlanie obrazów w złym hotelowym Wi-Fi trwa zbyt długo. Odpowiedzi na tego rodzaju pytania często można znaleźć w bardzo znajomym miejscu: narzędziach programistycznych przeglądarki.
Przez lata ulepszaliśmy narzędzia programistyczne, aby pomóc nam rozwiązywać tego rodzaju problemy z wydajnością w interfejsie naszych aplikacji. Przeglądarki mają teraz nawet wbudowane audyty wydajności. Może to pomóc w śledzeniu problemów z interfejsem, ale te audyty mogą ujawnić inne źródło spowolnienia, którego nie możemy naprawić w przeglądarce. Problemem są powolne czasy odpowiedzi serwera.
„Czas do pierwszego bajtu”
Bardzo niewiele optymalizacji przeglądarki może zrobić, aby ulepszyć stronę, która po prostu wolno się buduje na serwerze. Koszt ten jest ponoszony między przeglądarką wysyłającą żądanie o plik a otrzymaniem odpowiedzi. Analiza wykresu kaskadowego sieci w narzędziach programistycznych pokaże to opóźnienie w kategorii „Oczekiwanie (TTFB)”. Tyle czasu przeglądarka czeka między wysłaniem żądania a otrzymaniem odpowiedzi.
Pod względem wydajności jest to znane jako czas do pierwszego bajtu – czas, jaki zajmuje serwerowi wysyłanie czegoś, z czym przeglądarka może zacząć pracować. W tym czasie oczekiwania znajduje się wszystko, co serwer musi zrobić, aby zbudować stronę. W typowej witrynie może to obejmować kierowanie żądania do odpowiedniej części aplikacji, uwierzytelnianie żądania, wykonywanie wielu wywołań do systemów zaplecza, takich jak bazy danych i tak dalej. Może to obejmować uruchamianie treści przez systemy szablonów, wykonywanie wywołań API do usług stron trzecich, a może nawet wysyłanie wiadomości e-mail lub zmianę rozmiaru obrazów. Każda praca wykonywana przez serwer w celu zakończenia żądania jest zgnieciona w tym oczekiwaniu TTFB, którego użytkownik doświadcza w swojej przeglądarce.

Jak więc skrócić ten czas i szybciej zacząć dostarczać stronę do użytkownika? Cóż, to duże pytanie, a odpowiedź zależy od twojej aplikacji. To jest sama praca nad optymalizacją wydajności. To, co musimy najpierw zrobić, to zmierzyć wydajność, aby można było ocenić korzyści ze zmian.
Nagłówek czasu serwera
Zadaniem Server Timing nie jest pomoc w czasie aktywności na serwerze. Będziesz musiał sam ustalić czas, korzystając z dowolnego zestawu narzędzi, który udostępnia Ci platforma zaplecza. Celem synchronizacji serwera jest raczej określenie, w jaki sposób te pomiary mogą być przekazywane do przeglądarki.
Sposób, w jaki to się robi, jest bardzo prosty, przejrzysty dla użytkownika i ma minimalny wpływ na wagę Twojej strony. Informacje są wysyłane jako prosty zestaw nagłówków odpowiedzi HTTP.
Server-Timing: db;dur=123, tmpl;dur=56
Ten przykład komunikuje dwa różne punkty czasowe o nazwach db
i tmpl
. Nie są one częścią specyfikacji — są to nazwy, które wybraliśmy, w tym przypadku, aby reprezentować odpowiednio czasy bazy danych i szablonów.
Właściwość dur
podaje liczbę milisekund potrzebnych do zakończenia operacji. Jeśli spojrzymy na żądanie w sekcji Sieć w Narzędziach dla programistów, zobaczymy, że czasy zostały dodane do wykresu.

Nagłówek Server-Timing
może przyjmować wiele metryk oddzielonych przecinkami:
Server-Timing: metric, metric, metric
Każda metryka może określać trzy możliwe właściwości
- Krótka nazwa metryki (taka jak
db
w naszym przykładzie) - Czas trwania w milisekundach (wyrażony jako
dur=123
) - Opis (wyrażony jako
desc="My Description"
)
Każda właściwość jest oddzielona średnikiem jako ogranicznikiem. Do naszego przykładu moglibyśmy dodać opisy w następujący sposób:

Server-Timing: db;dur=123;desc="Database", tmpl;dur=56;desc="Template processing"

Jedyną wymaganą właściwością jest name
. Zarówno dur
, jak i desc
są opcjonalne i mogą być używane opcjonalnie w razie potrzeby. Na przykład, jeśli trzeba debugować problem z synchronizacją, który miał miejsce na jednym serwerze lub centrum danych, a nie na innym, przydatne może być dodanie tych informacji do odpowiedzi bez skojarzonego z nimi czasu.
Server-Timing: datacenter;desc="East coast data center", db;dur=123;desc="Database", tmpl;dur=56;desc="Template processing”
Pojawiłoby się to wtedy wraz z harmonogramami.

Jedną z rzeczy, które możesz zauważyć, jest to, że paski czasu nie są wyświetlane we wzorze kaskadowym. Dzieje się tak po prostu dlatego, że Server Timing nie próbuje komunikować sekwencji taktowania, tylko same surowe metryki.
Wdrażanie taktowania serwera
Dokładna implementacja we własnej aplikacji będzie zależeć od konkretnych okoliczności, ale zasady są takie same. Kroki zawsze będą następujące:
- Czas na niektóre operacje
- Zbierz razem wyniki pomiaru czasu
- Wyprowadź nagłówek HTTP
W pseudokodzie generowanie odpowiedzi może wyglądać tak:
startTimer('db') getInfoFromDatabase() stopTimer('db') startTimer('geo') geolocatePostalAddressWithAPI('10 Downing Street, London, UK') endTimer('geo') outputHeader('Server-Timing', getTimerOutput())
Podstawy implementacji czegoś podobnego powinny być proste w każdym języku. Bardzo prosta implementacja PHP może używać funkcji microtime()
do operacji pomiaru czasu i może wyglądać podobnie do poniższych.
class Timers { private $timers = []; public function startTimer($name, $description = null) { $this->timers[$name] = [ 'start' => microtime(true), 'desc' => $description, ]; } public function endTimer($name) { $this->timers[$name]['end'] = microtime(true); } public function getTimers() { $metrics = []; if (count($this->timers)) { foreach($this->timers as $name => $timer) { $timeTaken = ($timer['end'] - $timer['start']) * 1000; $output = sprintf('%s;dur=%f', $name, $timeTaken); if ($timer['desc'] != null) { $output .= sprintf(';desc="%s"', addslashes($timer['desc'])); } $metrics[] = $output; } } return implode($metrics, ', '); } }
Skrypt testowy używałby go w sposób przedstawiony poniżej, tutaj używając funkcji usleep()
do sztucznego tworzenia opóźnienia w działaniu skryptu, aby zasymulować proces, którego ukończenie wymaga czasu.
$Timers = new Timers(); $Timers->startTimer('db'); usleep('200000'); $Timers->endTimer('db'); $Timers->startTimer('tpl', 'Templating'); usleep('300000'); $Timers->endTimer('tpl'); $Timers->startTimer('geo', 'Geocoding'); usleep('400000'); $Timers->endTimer('geo'); header('Server-Timing: '.$Timers->getTimers());
Uruchomienie tego kodu wygenerowało nagłówek, który wyglądał tak:
Server-Timing: db;dur=201.098919, tpl;dur=301.271915;desc="Templating", geo;dur=404.520988;desc="Geocoding"

Istniejące wdrożenia
Biorąc pod uwagę, jak przydatne jest taktowanie serwera, istnieje stosunkowo niewiele implementacji, które mogłem znaleźć. Pakiet NPM synchronizacji czasu serwera oferuje wygodny sposób korzystania z synchronizacji serwera z projektów węzłów.
Jeśli używasz frameworka PHP opartego na oprogramowaniu pośredniczącym, tuupola/server-timing-middleware również zapewnia przydatną opcję. Używam tego w produkcji na Notist od kilku miesięcy i zawsze zostawiam włączone kilka podstawowych czasów, jeśli chcesz zobaczyć przykład na wolności.
Jeśli chodzi o obsługę przeglądarek, najlepsze, jakie widziałem, to Chrome DevTools i to właśnie wykorzystałem do zrzutów ekranu w tym artykule.
Rozważania
Sam czas serwera bardzo minimalizuje obciążenie odpowiedzi HTTP wysyłanej z powrotem przez sieć. Nagłówek jest bardzo minimalny i ogólnie można go bezpiecznie wysyłać, nie martwiąc się o kierowanie tylko do użytkowników wewnętrznych. Mimo to warto zachować krótkie nazwy i opisy, aby nie dodawać niepotrzebnych kosztów.
Bardziej niepokojąca jest dodatkowa praca, którą możesz wykonywać na serwerze, aby zmierzyć czas swojej strony lub aplikacji. Dodanie dodatkowego czasu i rejestrowania może samo w sobie mieć wpływ na wydajność, więc warto zaimplementować sposób włączania i wyłączania tego, gdy jest to wymagane.
Używanie nagłówka czasu serwera to świetny sposób na upewnienie się, że wszystkie informacje o taktowaniu zarówno z frontonu, jak i zaplecza aplikacji są dostępne w jednym miejscu. Zakładając, że Twoja aplikacja nie jest zbyt złożona, jej wdrożenie może być łatwe i możesz zacząć działać w bardzo krótkim czasie.
Jeśli chcesz przeczytać więcej o taktowaniu serwera, możesz wypróbować następujące rozwiązania:
- Specyfikacja taktowania serwera W3C
- Strona MDN dotycząca synchronizacji serwera zawiera przykłady i aktualne informacje o obsłudze przeglądarek
- Interesujący komentarz zespołu BBC iPlayer na temat korzystania z funkcji Server Timing.