Jak pisać lepsze testy obciążeniowe w Pythonie z Locust
Miałem już dość istniejących narzędzi do benchmarkingu i testów obciążeniowych serwera, więc znalazłem sobie nowe narzędzie open source o nazwie Locust. W tym artykule pokażę Ci, jak go poprawnie używać, aby uzyskać jak najlepsze rezultaty z testów obciążeniowych - dowiesz się tutaj m.in. ile żądań na sekundę (lub RPS, czyli requests per seconds) obsługuje Twój serwer. A zgodnie z oficjalną dokumentacją:
“… Locust to proste w użyciu, rozproszone narzędzie do testów obciążeniowych. Jest ono przeznaczone do testowania stron internetowych (lub innych systemów) i ustalania, ilu użytkowników jednocześnie może obsłużyć dany system. Jeśli chodzi o działanie, to podczas testu ‘szarańcza’ (ang. locust) zaatakuje Twoją witrynę. Zachowanie każdej szarańczy (lub użytkownika testowego) jest definiowane przez Ciebie, a proces będzie monitorowany z poziomu UI w czasie rzeczywistym. Pomoże Ci to w przeprowadzaniu testów oraz w zidentyfikowaniu wąskich gardeł w kodzie przed wpuszczeniem prawdziwych użytkowników. Locust jest całkowicie oparty na eventach, dlatego na jednym komputerze można obsługiwać tysiące użytkowników jednocześnie. W przeciwieństwie do wielu innych aplikacji opartych na zdarzeniach Locust nie używa wywołań zwrotnych. Zamiast tego wykorzystuje lekkie procesy, poprzez gevent. Każda szarańcza w Twojej witrynie działa w ramach własnego procesu (lub, aby być poprawnym, greenletu). Pozwala to na pisanie w Pythonie bardzo ekspresyjnych scenariuszy, bez komplikowania kodu za pomocą wywołań zwrotnych ”.
Artykuł ten podzielony jest na 4 sekcje:
- Konfiguracja
- Podstawowe użycie
- Wiersz poleceń
- Podsumowanie
Zaczynajmy!
1. Konfiguracja
Instalacja jest dość prosta, ponieważ Locust jest obsługiwany w wersjach 3.6, 3.7 i 3.8 (począwszy od wersji 0.14). Możesz po prostu uruchomić polecenie pip install, aby zainstalować Locust w oparciu o Twoją wersję Pythona. Zdecydowanie polecam najpierw skonfigurować środowisko wirtualne.
Python 3
python3 -m pip install locustio
Najnowsza wersja z git
python3 -m pip install -e
git://github.com/locustio/locust.git@master#egg=locustio
Powyższe powinno działać na Linuxie i Windowsie. Jeśli coś nie działa zgodnie z oczekiwaniami, to wypróbuj następującą metodę.
Opcjonalny fix dla Windowsa
Najpierw przejdź tutaj, aby pobrać gotowe binarki dla:
- pyzmq
- gevent
- greenlet
Otrzymasz plik wheel
, który instaluje się, uruchamiając następujące polecenie dla każdego z pakietów:
pip install name-of-the-package.whl
Gdy skończysz, uruchom pip install
.
pip install locustio
MacOS
MacOS wymaga zainstalowania programu gevent, zanim będzie można wykonać pip install. Najlepiej najpierw zainstalować Homebrew:
/usr/bin/ruby -e "$(curl -fsSL
https://raw.githubusercontent.com/Homebrew/install/master/install)"
A potem libev
, który jest zależnością gevent:
brew install libev
Następnie zainstaluj Locust za pomocą pip install
.
Sprawdzenie instalacji Locust
Gdy skończysz, możesz przetestować Locust, uruchamiając następujący kod w wierszu poleceń:
locust --help
Oto co powinno się pokazać:
W następnej sekcji omówimy podstawowe funkcje, które oferuje nam ten moduł i przyjrzymy się, jak napisać skrypt do testów obciążeniowych własnego serwera.
2. Podstawowe użycie
Serwer Flask
Załóżmy, że masz następujący plik serwera Flask - myapp.py
i jest on już gotowy do testu obciążeniowego:
from flask import Flask
server_port = 5000
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
if __name__ == "__main__":
app.run('0.0.0.0',port=server_port)
Uruchom serwer za pomocą następującej komendy w terminalu:
python myapp.py
Masz teraz dostęp do serwera przez localhost:5000
i widzisz Hello World.
Skrypt Locust
Utwórz plik o nazwie locustfile.py
. Dodaj do niego następującą instrukcję importu:
from locust import HttpUser, task, between
Kontynuuj pisanie klasy, która dziedziczy HttpUser
. Możesz ją nazwać, jak chcesz.
class WebsiteTestUser(HttpUser):
Wewnątrz klasy możesz zdefiniować własne funkcje, które służą jako task dla Locust. Zapewnia on dwie klasy bazowe, które będą wywoływane podczas uruchamiania i zatrzymywania wywołania Locust
:
- on_start
- on_stop
Jeśli dodasz do funkcji dekorator @task
, to zostanie ona potraktowana jako task dla Locust. Poniższy przykład przedstawia takie zadanie, które testuje API localhost:5000
:
@task(1)
def hello_world(self):
self.client.get("http://localhost:5000")
Musisz zmodyfikować numer dla kolejnych zadań. Oto jak to zrobić:
@task(1)
def hello_world(self):
self.client.get("http://localhost:5000")
@task(2)
def index(self):
self.client.get("http://localhost:5000/index")
Jeśli chcesz stworzyć task, który używa metody POST
, to użyj następującego kodu:
self.client.post("/login", {"username":"admin", "password":"admin"})
Gdy już skończysz, dodaj dodatkowe funkcje wewnątrz klasy, takie jak wait_time
:
wait_time
- to czas oczekiwania podczas wykonywania zadania. Teraz wykorzystamybetween(0.5, 3.0)
, które wskazuje na czas oczekiwania między 0,5 a 3 sekundami. Inne wbudowane funkcje, takie jak constant, czyconstant_pacing
, również są dostępne. Na potrzeby testów obciążeniowych, liczbę zamienimy na0
.
wait_time = between(0.5, 3.0)
Finalnie, Twój kod powinien wyglądać następująco:
from locust import HttpUser, task, between
class WebsiteTestUser(HttpUser):
wait_time = between(0.5, 3.0)
def on_start(self):
""" on_start is called when a Locust start before any task is scheduled """
pass
def on_stop(self):
""" on_stop is called when the TaskSet is stopping """
pass
@task(1)
def hello_world(self):
self.client.get("http://localhost:5000")
Funkcje on_start
i on_stop
są specjalnie puste, aby można się było do nich później odwołać. Jednym z ważniejszych przypadków użycia będzie dodanie do tych funkcji wywołań login
i logout
, aby przetestować uwierzytelnienie strony.
Test
Przetestujmy to przy pomocy kolejnej komendy, którą skierujemy do katalogu, gdzie znajduje się locustfile.py
. Jeśli korzystasz z takiej samej nazwy, to po prostu uruchom:
locust
Jeśli jednak korzystasz z innej nazwy, użyj następującej komendy i pamiętaj o odpowiedniej modyfikacji nazwy pliku:
locust -f personal_directory/your_file.py
Oto co powinno się pokazać:
http://localhost:8089/
Otwórz przeglądarkę i wprowadź następujący adres URL:
Powinniście zobaczyć takie oto UI. Najlepszą praktyką byłoby użycie adresu IP lub nazwy hosta. Na razie wystarczy jednak, że wypełnisz wszystko, co chcesz - w tym momencie nie wykonujemy żadnych rozproszonych testów.
Naciśnij start swarming, kiedy będziesz gotów. Proces się rozpocznie, a Ty zobaczysz takie oto zmiany:
- Request - łączna ilość żądań, które do tej pory wykonano
- Fails - ilość żądań, które zakończyły się niepowodzeniem
- Median - szybkość odpowiedzi dla 50 percentyla w milisekundach
- 90%ile - szybkość odpowiedzi dla 90 percentyla w milisekundach
- Average - średnia szybkość odpowiedzi w milisekundach
- Min - minimalna szybkość odpowiedzi w milisekundach
- Max - maksymalna prędkość odpowiedzi w milisekundach
- Average bytes - średni rozmiar odpowiedzi w bajtach
- Current RPS - obecna ilość żądań na sekundę
- Current Failure/s - łączna ilość niepowodzeń na sekundę
Przejdź do zakładki chart. Oto co powinno się tam pokazać:
Reszta kart jest zarezerwowana dla niepowodzeń i wyjątków. Możesz też pobrać dane w formacie csv z zakładki Download Data. Widzisz pewnie, że RPS jest teraz zbyt niski. Dzieje się tak, ponieważ ustawiliśmy wait-time
na między 0,5 a 3 sekundy. Jeśli chodzi o testy obciążeniowe, to musimy zmienić obie wartości na 0
. Możesz tutaj użyć funkcji constant(0)
.
Zrestartuj swój test - zobaczysz wtedy maksymalny RPS, który serwer mógłby udźwignąć. Możesz tutaj śmiało eksperymentować, korzystając z różnych ilości użytkowników oraz różnego hatch rate. Następna sekcja symuluje tę samą funkcję bezpośrednio z poziomu wiersza poleceń, a nie z webowego UI.
3. Wiersz poleceń
Moduł ten zapewnia sposób na uruchomienie testów obciążeniowych z poziomu wiersza poleceń. Pozwala to na łatwe zaimplementowanie testów obciążeniowych uruchamianych automatycznie. Dodaj parametry no-web podczas uruchamiania Locust. Musisz również włączyć w to wszystko hosta, liczbę klientów oraz hatch rate.
locust -f locustfile.py --no-web --host wfng -c 1000 -r 100
f
- ścieżka do plikuno-web
- uruchom symulację bez interfejsu webowegoc
- liczba klientówr
— Hatch rate
Uruchamiaj testy przez około 30 sekund. Test po uruchomieniu można przerwać, naciskając Ctrl+C. Wtedy powinno się pokazać:
Mamy też tutaj inne przydatne parametry:
t
- wstrzymuje test po określonym czasie (30 sekund, 5 minut, 60 minut, 90 minut)csv
- zapisuje wyniki w plikach o formacie CSV
Możesz sprawdzić wszystkie dostępne parametry dzięki:
locust --help
4. Podsumowanie
Podsumujmy to, czego się tutaj nauczyliśmy. Po pierwsze, dokonaliśmy prostej instalacji modułu, w zależności od używanej przez nas platformy. Następnie stworzyliśmy testowy serwer Flask dla testów obciążeniowych. Co więcej, przyjrzeliśmy się funkcji dostarczonej przez moduł, pisząc prosty skrypt Locust.
Wykonaliśmy test i otrzymaliśmy wyniki w webowym UI. Dane i tabele można z łatwością pobrać w formacie CSV. Spróbowaliśmy też uruchomić taką samą symulację z poziomu wiersza poleceń. Pozwoliło to na uruchomienie zautomatyzowanego przepływu dla testów obciążeniowych.
Dziękuję za uwagę!
Oryginał tekstu w języku angielskim możesz przeczytać tutaj.