Wie wir das Framework für Leistungstests ausgewählt und verdreht haben

Es gibt viele Möglichkeiten, APIs und Schnittstellen zu testen. Im Zusammenhang mit der Eröffnung eines breiten Zugangs zur Acronis Cyber ​​Platform mussten wir nach Möglichkeiten suchen, die Dienste von verschiedenen Positionen aus auf „Haltbarkeit“ zu testen. In diesem Beitrag spricht der leitende Softwarearchitekt von Acronis, Dmitry Salomatin, darüber, wie wir das Framework für das Testen ausgewählt haben, auf welche Schwierigkeiten wir gestoßen sind und welche Verbesserungen wir selbst vornehmen mussten.

Bild

Ich muss sofort sagen, dass wir bei Acronis beim Testen von APIs besonders vorsichtig sind. Tatsache ist, dass unsere eigenen Produkte über dieselben APIs auf Services zugreifen, die für die Verbindung externer Systeme verwendet werden. Daher ist ein Leistungstest für jede Schnittstelle erforderlich. Wir testen den Betrieb der API und isolieren den Betrieb der Benutzeroberfläche isoliert. Mithilfe der Testergebnisse können Sie bewerten, ob die API selbst sowie die Benutzeroberflächen gut funktionieren. Bestätigen Sie die erfolgreiche Entwicklung oder formulieren Sie eine Aufgabe für die weitere Entwicklung.

Aber die Tests unterscheiden sich. Manchmal zeigt ein Dienst nicht sofort eine Verschlechterung. Selbst wenn wir einen Service ausführen, der den bereits in der Version veröffentlichten Produkten ähnelt, können Sie ihn zur Überprüfung mit denselben Daten laden, die auch in "prod" verwendet werden. In diesem Fall können Sie die Regression sehen, aber es ist absolut unmöglich, die Perspektive zu beurteilen. Sie wissen einfach nicht, was passieren wird, wenn die Datenmenge stark zunimmt oder die Häufigkeit von Anfragen zunimmt.

Das folgende Diagramm zeigt, wie sich die Anzahl der vom Backend pro Sekunde verarbeiteten APIs mit dem Datenwachstum im System ändert



Angenommen, der von uns getestete Dienst befindet sich in einem für den Beginn dieses Zeitplans typischen Zustand. In diesem Fall nimmt die Geschwindigkeit dieser API selbst bei einem geringen Wachstum des Systems stark ab.

Um solche Situationen auszuschließen, erhöhen wir die Datenmenge um ein Vielfaches und erhöhen die Anzahl der parallelen Threads, um zu verstehen, wie sich der Dienst verhält, wenn die Auslastung dramatisch zunimmt.

Aber es gibt noch eine Nuance. Wenn sich die Arbeit eines „vertrauten“ Dienstes mit der Zunahme der Datenmenge, seiner Entwicklung, der Entstehung neuer Funktionen und mit neuen Diensten ändert, wird die Situation noch komplizierter. Wenn ein konzeptionell neuer Service in einem Produkt erscheint, muss er aus vielen verschiedenen Blickwinkeln betrachtet werden. In dieser Situation müssen Sie spezielle Datensätze vorbereiten, Lasttests durchführen und mögliche Anwendungsfälle vorschlagen.

Leistungsmerkmale von Acronis


In der Regel finden unsere Testprozesse in einem „Spiralmuster“ statt. In einer der Testphasen wird die API verwendet, um die Anzahl der Entitäten zu erhöhen (Größenbestimmung), und in der zweiten Phase werden neue Vorgänge für vorhandene Datasets ausgeführt (Verwendung). Alle Tests werden in einer anderen Anzahl von Threads ausgeführt. Zum Beispiel haben wir den Animals-Dienst und er hat die folgenden APIs:

POST /animals PUT /animals/<id> GET /animals?filter=criteria 

1 und 2 sind APIs, die in Dimensionierungstests aufgerufen werden. Sie erhöhen die Anzahl neuer Entitäten im System.
3 sind APIs, die in der Nutzungsphase aufgerufen werden. Diese API verfügt über eine Vielzahl von Filteroptionen. Dementsprechend wird es mehr als einen Test geben

Durch iterative Größen- und Verwendungstests erhalten wir ein Bild von der Änderung der Systemleistung mit ihrem Wachstum






Framework benötigt ...


Um eine große Anzahl neuer und aktualisierter Dienste in großem Maßstab testen zu können, benötigten wir ein flexibles Framework, mit dem wir verschiedene Skripte ausführen können. Und die Hauptsache ist, die API wirklich zu testen und nicht nur Services mit sich wiederholenden Vorgängen zu belasten.

Leistungstests können sowohl an einer synthetischen Ladung als auch anhand eines aus der Produktion aufgezeichneten Lastmusters durchgeführt werden. Beide Ansätze haben Vor- und Nachteile. Eine Methode mit einer realen Last kann eher als Stresstest bezeichnet werden. Wir erhalten ein reales Bild der Systemleistung unter einer solchen Last, können jedoch Problembereiche nicht leicht identifizieren, den Durchsatz der Komponenten nicht separat messen, sondern erhalten nicht die genauen Zahlen, denen die einzelnen Komponenten standhalten können. Beim synthetischen Ansatz erhalten wir genaue Zahlen, verfügen über eine große Flexibilität und können problemlos Problembereiche beheben. Indem wir mehrere Testskripten parallel ausführen, können wir die Belastungslast reproduzieren. Die Hauptnachteile des zweiten Ansatzes sind die hohen Arbeitskosten für das Schreiben von Testskripten sowie das wachsende Risiko, wichtige Skripte zu verpassen. Deshalb haben wir uns für den schwierigeren Weg entschieden.

Die Wahl eines Frameworks wurde also von der Aufgabe bestimmt. Und unsere Aufgabe ist es:

  • Auffinden von API-Engpässen
  • Prüfen Sie den Widerstand gegen hohe Lasten
  • Um die Effektivität des Dienstes mit dem Wachstum des Datenvolumens zu bewerten
  • Identifizieren Sie kumulative Fehler, die im Laufe der Zeit auftreten

Es gibt so viele Performance-Frameworks auf dem Markt, die eine große Anzahl identischer Anforderungen auslösen können. Viele von ihnen erlauben keine Änderung innerhalb (z. B. Apache Benchmark) oder mit eingeschränkten Funktionen zur Beschreibung von Skripten (z. B. JMeter).

Wir verwenden normalerweise komplexere Skripte beim Testen. Oft müssen API-Aufrufe nacheinander ausgeführt werden - nacheinander oder um die Anforderungsparameter gemäß einer Logik zu ändern. Das einfachste Beispiel, wenn wir eine REST-API des Formulars testen möchten

 PUT /endpoint/resource/<id> 

In diesem Fall müssen Sie im Voraus die <id> der Ressource kennen, die wir ändern möchten, um die Nettoausführungszeit der Abfrage zu messen.

Daher müssen wir die Möglichkeit haben, Skripte zu erstellen, um komplexe Testabfragen auszuführen.

Schneller


Da Acronis-Produkte für hohe Auslastung ausgelegt sind, testen wir APIs in Zehntausenden von Anforderungen pro Sekunde. Es stellte sich heraus, dass dies nicht in jedem Framework möglich ist. Zum Beispiel kann Python nicht immer und nicht immer zum Testen verwendet werden, da aufgrund der Besonderheiten der Sprache die Möglichkeit, eine große Multithread-Last zu erstellen, begrenzt ist

Ein weiteres Problem ist der Einsatz von Ressourcen. Als erstes haben wir uns das Locust-Framework angesehen, das von mehreren Hardwareknoten gleichzeitig ausgeführt werden kann und eine gute Leistung erzielt. Gleichzeitig werden jedoch viele Ressourcen für die Arbeit des Testsystems aufgewendet, und der Betrieb ist teuer.

Aus diesem Grund haben wir uns für das K6-Framework entschieden, mit dem wir Skripte in vollwertigem Javascript beschreiben und eine überdurchschnittliche Leistung erzielen können. Dieses Framework ist in Go geschrieben und gewinnt schnell an Popularität. Zum Beispiel hat das Projekt auf Github bereits fast 5,5 Tausend Sterne erhalten! K6 entwickelt sich aktiv weiter, und die Community hat bereits fast dreitausend Commits vorgeschlagen. Das Projekt hat 50 Mitwirkende, die 36 Code-Zweige erstellt haben. Natürlich ist K6 noch lange nicht ideal, aber nach und nach wird das Framework besser, und Sie können hier über den Vergleich mit Jmeter lesen.

Schwierigkeiten und ihre Lösungen


Angesichts der „Jugendlichkeit“ von K6 standen wir auch nach einer ausgewogenen Wahl des Rahmens vor einer Reihe von Problemen. Bevor Sie beispielsweise eine API wie / endpoint / testen, müssen Sie diese Endpunkte zunächst irgendwie finden. Wir können nicht dieselben Werte verwenden, da die Ergebnisse aufgrund des Zwischenspeicherns falsch sind.

Sie können die benötigten Daten auf verschiedene Arten abrufen:

  • Sie können sie über die API anfordern
  • Sie können den direkten Zugriff auf die Datenbank verwenden

Die zweite Methode ist schneller und bei Verwendung relationaler DBs oftmals sehr viel praktischer, da Sie bei längeren Tests erheblich Zeit sparen können. Das einzige "aber" ist, dass Sie es nur verwenden können, wenn der Servicecode und die Tests von denselben Personen geschrieben wurden. Da zum Durcharbeiten der Datenbank die Tests immer auf dem neuesten Stand sein müssen. Im Fall von K6 verfügt das Framework jedoch nicht über Zugriffsmechanismen auf Datenbanken. Daher musste ich das entsprechende Modul selbst schreiben.

Ein weiteres Problem tritt beim Testen nicht idempotenter APIs auf. In diesem Fall ist es wichtig, dass sie nur einmal mit denselben Parametern aufgerufen werden (z. B. die DELETE-API). In unseren Tests bereiten wir Testdaten im Voraus in der Einrichtungsphase vor, wenn das System eingerichtet und vorbereitet wird. Während des Tests werden reine API-Aufrufe gemessen, da Zeit und Ressourcen für die Datenaufbereitung nicht mehr erforderlich sind. Dies wirft jedoch das Problem auf, vorbereitete Daten auf nicht synchronisierte Flüsse des Haupttests zu verteilen. Dieses Problem wurde erfolgreich durch Schreiben einer internen Datenwarteschlange behoben. Aber das ist ein ganz großes Thema, das wir in den nächsten Beiträgen diskutieren werden.

Bereites Framework


Zusammenfassend möchte ich festhalten, dass es nicht einfach war, ein komplett fertiges Gerüst zu finden, und ich musste noch einige Dinge mit meinen Händen erledigen. Trotzdem haben wir heute ein für uns geeignetes Tool, mit dem wir unter Berücksichtigung von Verbesserungen komplexe Tests durchführen und eine Simulation hoher Lasten erstellen können, um die Leistung der API und der GUI unter verschiedenen Bedingungen zu gewährleisten.

Im nächsten Beitrag werde ich darüber sprechen, wie wir das Problem des Testens eines Dienstes gelöst haben, der die gleichzeitige Verbindung von Hunderttausenden von Verbindungen mit minimalen Ressourcen unterstützt.

Source: https://habr.com/ru/post/de474694/


All Articles