Nasza strona używa cookies. Korzystając ze strony, wyrażasz zgodę na używanie cookies, zgodnie z aktualnymi ustawieniami przeglądarki. Rozumiem

Jak pisać lepsze testy obciążeniowe w Pythonie z Locust

Ng Wai Foong AI Engineer / YOOZOO GAMES
Poznaj Locust i sprawdź, jak narzędzie to pomoże Ci w uzyskaniu lepszych rezultatów ze swoich testów obciążeniowych w Pythonie.
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:

  1. Konfiguracja
  2. Podstawowe użycie
  3. Wiersz poleceń
  4. 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 wykorzystamy between(0.5, 3.0), które wskazuje na czas oczekiwania między 0,5 a 3 sekundami. Inne wbudowane funkcje, takie jak constant, czy constant_pacing, również są dostępne. Na potrzeby testów obciążeniowych, liczbę zamienimy na 0

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 pliku
  • no-web - uruchom symulację bez interfejsu webowego
  • c - liczba klientów
  • r — 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

Rozpocznij dyskusję

Lubisz dzielić się wiedzą i chcesz zostać autorem?

Podziel się wiedzą z 160 tysiącami naszych czytelników

Dowiedz się więcej