{"id":22034,"date":"2022-02-07T10:20:47","date_gmt":"2022-02-07T09:20:47","guid":{"rendered":"https:\/\/blog.mi.hdm-stuttgart.de\/?p=22034"},"modified":"2023-08-06T21:39:36","modified_gmt":"2023-08-06T19:39:36","slug":"entwicklung-und-benchmarking-einer-eigenen-elevation-api","status":"publish","type":"post","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/","title":{"rendered":"Entwicklung und Benchmarking einer eigenen Elevation API"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\" id=\"worum-geht-s\">Worum geht&#8217;s?<\/h2>\n\n\n\n<p>Im Rahmen der Veranstaltung <a href=\"https:\/\/www.hdm-stuttgart.de\/studierende\/stundenplan\/studieninhalte\/vorlesung_detail?ansicht=1&amp;vorlid=5213868&amp;sgbvsid=5168447\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"&quot;System Engineering and Management&quot;\">&#8220;System Engineering and Management&#8221;<\/a> sollte ein Softwareprojekt unserer Wahl und mit besonderem Augenmerk auf Systemarchitektur durchgef\u00fchrt, analysiert und dokumentiert werden. F\u00fcr unser Projekt haben wir uns entschieden, einen besonderen Schwerpunkt auf Monitoring zu legen. Das Projekt bestand also aus drei gr\u00f6\u00dferen Teilprojekten: dem Backend selbst, ein Stresser, der Last f\u00fcr das Backend erzeugt und das zugeh\u00f6rige Monitoring. Im Folgenden m\u00f6chten wir das Ergebnis dieses Semesters aber vor allem auch den Weg dahin beschreiben.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"hoch-hinaus\">Hoch hinaus&#8230;<\/h2>\n\n\n\n<!--more-->\n\n\n\n<p>Bevor wir uns \u00fcber die Architektur Gedanken machen konnten, musste zuerst einmal eine Anwendungsidee her. Wir haben uns hier f\u00fcr eine Elevation API entschieden. Aber was ist denn eine Elevation API? Eine Elevation API ist eine API, die einem zu einem durch L\u00e4ngen- und Breitengrad definierten Punk der Erdoberfl\u00e4che die H\u00f6he des entsprechenden Punkts \u00fcber normal Null ausgibt. (Z.B. hat der Punkt mit den Koordinaten 48.742275\u00b0 Nord, 9.101177\u00b0 Ost (Haupteingang der HdM) die H\u00f6he 459 m. Derartige H\u00f6heninformationen werden f\u00fcr viele Anwendungen ben\u00f6tigt. Man kann sich die H\u00f6he eines Punktes auch einfach in Google Earth anzeigen lassen, indem man mit der Maus \u00fcber einen Punkt f\u00e4hrt. Die H\u00f6he wird dann unten rechts angezeigt.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/ElevationExample.png\"><img loading=\"lazy\" decoding=\"async\" width=\"956\" height=\"609\" data-attachment-id=\"22404\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/elevationexample\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/ElevationExample.png\" data-orig-size=\"956,609\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"ElevationExample\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/ElevationExample.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/ElevationExample.png\" alt=\"\" class=\"wp-image-22404\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/ElevationExample.png 956w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/ElevationExample-300x191.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/ElevationExample-768x489.png 768w\" sizes=\"auto, (max-width: 956px) 100vw, 956px\" \/><\/a><figcaption class=\"wp-element-caption\">Beispiel einer H\u00f6henmessung vor dem Haupteingang der HdM in Google Earth<\/figcaption><\/figure>\n\n\n\n<p>Wie kamen wir also nun auf die Idee, unsere eigene Elevation API zu entwickeln? Nun, wenn man mit Google Earth arbeitet, bekommt man hier recht schnell ein Problem. Denn w\u00e4hrend man sich die H\u00f6heninformationen in Google Earth selbst problemlos anzeigen lassen kann, werden sie nicht exportiert. Das hei\u00dft, wenn man z.B. einen Pfad in Google Earth definiert und dann f\u00fcr die weitere Verwendung in einer anderen Anwendung exportiert, werden alle Punkte mit der H\u00f6he 0m ausgegeben (Gleichzeitig bietet Google eine eigene [kostenpflichtige] Elevation API an. Ein Schelm, wer b\u00f6ses dabei denkt&#8230;\ud83d\ude42). Wer also H\u00f6heninformationen f\u00fcr seine Anwendung braucht, muss sich diese auf anderem Wege beschaffen. Daf\u00fcr kann man z.B. auf kostenlose Elevation APIs wie <a href=\"https:\/\/www.opentopodata.org\/\" title=\"OpenTopoData \">OpenTopoData<\/a> zur\u00fcckgreifen. Dieses Konzept hatte uns also neugierig gemacht, sodass wir uns an die Entwicklung unserer eigenen Elevation API gemacht haben&#8230;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"das-backend\">Das Backend<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"die-plattform\">Die Plattform<\/h3>\n\n\n\n<p>Nachdem die Idee f\u00fcr eine Anwendung nun feststand, war die erste gr\u00f6\u00dfere technische Entscheidung, welche Plattform wir f\u00fcr das Backend benutzen wollten. Hier haben wir uns recht schnell f\u00fcr ASP.NET Core und C# entschieden, da wir bereits Erfahrung mit C# (und teilweise auch mit ASP.NET Core) hatten. Hier spielte also eher Bequemlichkeit, als technische Aspekte eine Rolle. Ein Fehler, der uns sp\u00e4ter an einer Stelle noch geh\u00f6rig Kopfschmerzen bereiten sollte. F\u00fcrs Erste entwickelten wir aber munter darauf los&#8230;<\/p>\n\n\n\n<figure class=\"wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex\">\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/NET_Core_Logo.svg_-2.png\"><img loading=\"lazy\" decoding=\"async\" width=\"512\" height=\"512\" data-attachment-id=\"22100\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/net_core_logo-svg_-2\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/NET_Core_Logo.svg_-2.png\" data-orig-size=\"512,512\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"NET_Core_Logo.svg_-2\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/NET_Core_Logo.svg_-2.png\" data-id=\"22100\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/NET_Core_Logo.svg_-2.png\" alt=\"\" class=\"wp-image-22100\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/NET_Core_Logo.svg_-2.png 512w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/NET_Core_Logo.svg_-2-300x300.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/NET_Core_Logo.svg_-2-150x150.png 150w\" sizes=\"auto, (max-width: 512px) 100vw, 512px\" \/><\/a><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Csharp_Logo-1.png\"><img loading=\"lazy\" decoding=\"async\" width=\"512\" height=\"512\" data-attachment-id=\"22102\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/csharp_logo-1\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Csharp_Logo-1.png\" data-orig-size=\"512,512\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Csharp_Logo-1\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Csharp_Logo-1.png\" data-id=\"22102\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Csharp_Logo-1.png\" alt=\"\" class=\"wp-image-22102\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Csharp_Logo-1.png 512w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Csharp_Logo-1-300x300.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Csharp_Logo-1-150x150.png 150w\" sizes=\"auto, (max-width: 512px) 100vw, 512px\" \/><\/a><\/figure>\n<\/figure>\n\n\n\n<p>An dieser Stelle machten wir uns zu Nutze, dass Visual Studio (unsere IDE) bereits Vorlagen f\u00fcr Konfigurationen enth\u00e4lt, um die Anwendung in einem Docker Container zu hosten. Wir mussten also nur die entsprechende Vorlage bei der Projekterstellung ausw\u00e4hlen und Visual Studio erledigte den Rest (erzeugt z.B. das Dockerfile). Es gibt hier sogar die M\u00f6glichkeit, bei jedem App-Start auszuw\u00e4hlen, ob man die Anwendung im Container oder doch lieber nativ auf dem Host ausf\u00fchren will. Mehr Flexibilit\u00e4t kann man sich also nicht w\u00fcnschen. Allerdings hat das, wie so oft, wenn einem die IDE Arbeit abnimmt, auch den Nachteil, dass man, wenn man mit der Materie (in diesem Fall Docker) noch nicht so vertraut ist, nicht unbedingt alles versteht, was das unter der Haube passiert, sodass man dann, wenn man etwas konfigurieren m\u00f6chte, erst Nachforschungen anstellen muss, wie das funktioniert. Das ist zwar heutzutage (aufgrund von stackoverflow.com und co.) recht einfach. Dennoch sollte man sich idealerweise, bevor man mit solchen Presets arbeitet, grundlegend in die Materie einarbeiten, um die eigene Anwendung nachher besser zu verstehen und Zeit bei der Entwicklung zu sparen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"eine-gewohnliche-rest-api\">Eine gew\u00f6hnliche REST-API<\/h3>\n\n\n\n<p>Die grunds\u00e4tzliche Struktur des Backends ist recht simpel. Ein einfacher Web-Server (ASP-NET Core verwendet Kestrel) mit einem Endpunkt zur Abfrage von H\u00f6hendaten. Der Endpunkt unterst\u00fctzt sowohl GET-Requests, in welchen die Koordinaten in der URL stehen, also auch POST-Requests mit den Koordinaten im Body (JSON-Format). Dabei k\u00f6nnen auch mehrere Koordinaten gleichzeitig angefragt werden.<\/p>\n\n\n\n<p>Beispiele f\u00fcr Anfragen:<br><strong>GET:<\/strong> http:\/\/localhost:8080\/api\/elevation?coordinates=48.00,9.00;-12.34,-23.45<br><strong>POST:<\/strong> localhost:8080\/api\/elevation<br><em>[Body]<\/em><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">&#091;    \n    {\n        &quot;latitude&quot;: 48,\n        &quot;longitude&quot;: 9    \n    },\n    {        \n        &quot;latitude&quot;: 49.1,\n        &quot;longitude&quot;: 10.2\n    }\n]<\/code><\/pre>\n\n\n\n<p>Als Ergebnis bekommt man (sowohl f\u00fcr GET-, als auch f\u00fcr POST-Reqests) im Erfolgsfall eine Antwort in der Form:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">&#091;\n    {\n        &quot;latitude&quot;: 48,\n        &quot;longitude&quot;: 9,\n        &quot;altitude&quot;: 739.7893850836467\n    },\n    {\n        &quot;latitude&quot;: 49.1,\n        &quot;longitude&quot;: 10.2,\n        &quot;altitude&quot;: 492.73202840246756\n    }\n]<\/code><\/pre>\n\n\n\n<p>Im Fehlerfall wird ein entsprechender HTTP Statuscode zur\u00fcckgegeben.<\/p>\n\n\n\n<p>F\u00fcr die Bearbeitung einer Anfrage werden auf dem Server grob zusammengefasst zwei wichtige Schritte ausgef\u00fchrt. Zun\u00e4chst wird die Anfrage eingelesen und in Koordinatenpunkte umgewandelt. Sollte dabei ein Fehler auftraten (z.B. wegen eine ung\u00fcltigen Anfrage [z.B. ein Buchstabe in einer Koordinate] oder falsche Formatierung]) wird direkt ein Fehler zur\u00fcckgegeben. Diese Pr\u00fcfung geschieht der Einfachheit halber (in unserer kleinen Anwendung) direkt im Elevation Controller (Controller sind in ASP.NET Core die Komponenten, die die HTTP-Anfragen erhalten und f\u00fcr das Senden der Antwort zust\u00e4ndig sind.) Ist die Umwandlung erfolgreich, werden zu den Koordinatenpunkten die H\u00f6heninformationen ermittelt. Sobald das geschehen ist, wird die Antwort gesendet.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"der-kern-des-ganzen-die-hohendaten-und-ihre-verarbeitung\">Der Kern des Ganzen: die H\u00f6hendaten und ihre Verarbeitung<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"das-datenset\">Das Datenset<\/h4>\n\n\n\n<p>Um die H\u00f6heninformationen f\u00fcr eine Koordinate zu gewinnen, brauchten wir zun\u00e4chst die Rohdaten. Also Dateien, in welchen die H\u00f6hendaten f\u00fcr einen bestimmten Bereich gespeichert waren. F\u00fcr solche Daten gibt es mehrerer Quellen (u.a. von der NASA). Wir entschieden uns f\u00fcr den <a href=\"https:\/\/land.copernicus.eu\/imagery-in-situ\/eu-dem\/eu-dem-v1.1\" title=\"EU-DEM v1.1-Datensatz von Copernicus\">EU-DEM v1.1-Datensatz von Copernicus<\/a>. Copernicus bietet als europ\u00e4isches Projekt Daten in besonders hoher Aufl\u00f6sung (25 m x 25 m, d. h. alle 25 Meter ein Messpunkt) f\u00fcr den Bereich Europa.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/copernicus.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"535\" data-attachment-id=\"22093\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/copernicus\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/copernicus.png\" data-orig-size=\"2239,1169\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"copernicus\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/copernicus-1024x535.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/copernicus-1024x535.png\" alt=\"\" class=\"wp-image-22093\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/copernicus-1024x535.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/copernicus-300x157.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/copernicus-768x401.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/copernicus-1536x802.png 1536w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/copernicus-2048x1069.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Karten\u00fcbersicht \u00fcber den EU-DEM v1.1-Datensatz von Copernicus<\/figcaption><\/figure>\n\n\n\n<p>Die Daten wurden in Form von GeoTIFFs bereitgestellt. Das sind spezielle TIFF-Bilddateien, die die H\u00f6hendaten in den Pixeln enthalten und zus\u00e4tzlich bestimmte zus\u00e4tzliche Informationen wie z.B. das verwendete Koordinaten-Projektionssystem in den Metadaten. Wir haben also die Daten f\u00fcr den Bereich Deutschland heruntergeladen. Da diese Daten recht gro\u00df sind (ca. 8,3 GB nur f\u00fcr Deutschland), haben wir uns entschieden, uns f\u00fcr dieses Projekt auf Deutschland zu beschr\u00e4nken. <strong><em>(ACHTUNG: Das bedeutet, dass Anfragen f\u00fcr Koordinaten au\u00dferhalb Deutschlands (bzw. der Koordinatenbl\u00f6cke im Datensatz, welche Deutschland enthalten [E40N20 &amp; E40N30 -&gt; siehe Karte]) nicht funktionieren.)<\/em><\/strong><\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"gdal-der-troublemaker\">GDAL&#8230;der Troublemaker<\/h4>\n\n\n\n<p>Als n\u00e4chsten, mussten wir den Zugriff auf die H\u00f6hendaten implementieren. Um die verschiedenen GeoTIFF-Dateien zu einem Raster zusammenzuf\u00fcgen und aus diesem, H\u00f6heninformationen abzufragen, entschieden wir uns, die bekannte Bibliothek <a href=\"https:\/\/gdal.org\/\" title=\"GDAL\">GDAL<\/a> zu verwenden.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/08\/gdalicon.png\" alt=\"\"\/><figcaption class=\"wp-element-caption\">GDAL Logo<\/figcaption><\/figure>\n\n\n\n<p>Bis hierher verlief die Entwicklung des Backends relativ fl\u00fcssig. Hier aber fiel uns jetzt die Wahl der Plattform auf die F\u00fc\u00dfe. GDAL ist f\u00fcr derartige Raster-Aufgaben, wie wir sie in unserer Anwendung brauchten, wie geschaffen und auch weit verbreitet. Allerdings handelt es sich bei GDAL um eine <strong>Python<\/strong>-Bibliothek. Die direkte Einbindung in unsere Anwendung war also nicht m\u00f6glich. Wir brauchten also einen .NET Core Port von GDAL. Wir entschieden uns f\u00fcr das Paket <em>MaxRev.Gdal.Core<\/em>. <\/p>\n\n\n\n<p>Die Implementierung der n\u00e4chsten ca. 20 Zeilen Code dauerte einige Stunden.<\/p>\n\n\n\n<p>Im Grunde war das, was wir zu erreichen versuchten, nicht sehr kompliziert. Wir wollten &#8211; wie gesagt &#8211;  die GeoTIFF-Daten in ein Raster zusammenf\u00fcgen und H\u00f6heninformationen daraus abfragen. Das l\u00e4sst sich mit GDAL in etwa 20 Zeilen Code realisieren. ABER: da wir nicht GDAL direkt, sondern einen Port verwendeten, wich die Klassen- und Methodennamen oft von der (dokumentierten) Originalversion von GDAL ab. Und da der von uns verwendete (aber auch alle anderen) GDAL-Port nicht sehr weit verbreitet ist, gab es auch kaum Hilfestellungen dazu im Internet (z.B. Foreneintr\u00e4ge). Erschwerend kam noch dazu, dass solche Ports oft unvollst\u00e4ndig sind und einige Funktionalit\u00e4ten des Originals nicht implementiert sind, was f\u00fcr noch mehr Verwirrung und Unsicherheit sorgte. Wir mussten uns also jede Zeile Code durch Probieren und aufw\u00e4ndige Internetrecherche hart erarbeiten. Wir haben sogar andere GDAL-Ports ausprobiert, die aber alle noch schlechter funktionierten.<\/p>\n\n\n\n<p>Am Ende waren wir froh, dass wir funktionierenden Code zusammenbekommen hatten, der unsere Aufgabe erf\u00fcllte. W\u00fcrden wir unsere Anwendung jetzt allerdings erweitern und neue Funktionalit\u00e4t implementieren wollen, st\u00fcnden wir wieder vor dem gleichen Problem.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"koordinate-ist-nicht-gleich-koordinate\">Koordinate ist nicht gleich Koordinate<\/h4>\n\n\n\n<p>Ein weiteres Hindernis, das es zu bew\u00e4ltigen gab, war, dass der EU-DEM v1.1-Datensatz ein anderes Projektionssystem (ETRS89 \/ LAEA Europe), als das gew\u00f6hnliche L\u00e4ngen-\/Breitengrassystem (WGS 84) verwendet. Wir mussten also erst einen Weg finden, die vom Benutzer angefragten Koordinaten in L\u00e4ngen- und Breitengrad in Koordinaten im ETRS89 \/ LAEA Europe-System umzuwandeln. Auch hier hatten wir es wieder mit der selben Problematik wie bei GDAL zu tun. Es gab zwar einige Anleitungen im Internet, wie diese Koordinatenumrechnung zu bewerkstelligen war, jedoch bezog sich keine davon auf .NET Core und C#. Auch hier mussten wir uns am Ende wieder auf eine kleine Drittanbieter-Bibliothek (<em>DotSpatial.Projections<\/em>) verlassen, wobei Dokumentation und Communityinhalte hier ebenso d\u00fcrftig waren, wie bei GDAL, sodass auch die Implementierung dieses Codes deutlich mehr Zeit in Anspruch nahm, als uns das lieb gewesen w\u00e4re.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"enbindung-der-daten-in-den-container\">Enbindung der Daten in den Container<\/h4>\n\n\n\n<p>Am Ende war es jedoch soweit, und wir hatten unsere funktionierende Anwendung. Jetzt mussten wir nur noch einen Weg finden, die H\u00f6hendaten (in Form der zwei GeoTIFF-Dateien und einer (von GDAL) generierten VRT-Rasteratei) in unserem Container zur Verf\u00fcgung zu stellen. Eine M\u00f6glichkeit w\u00e4re gewesen, die Dateien einfach in das Docker-Image zu packen. Da die Dateien aber, wie gesagt, mehrere GB gro\u00df waren, h\u00e4tte das das Image unn\u00f6tig aufgeblasen und unhandlich gemacht. Stattdessen entschieden wir uns, die Dateien auf dem Host zu speichern und den entsprechenden Ordner im Container zu mounten. Dies kann durch Angabe eines Parameters beim Start des Docker-Containers bewerkstelligt werden:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">docker run &lt;strong&gt;--volume D:\/elevation\/data:\/app\/data:ro&lt;\/strong&gt;<\/code><\/pre>\n\n\n\n<p>Dieser Parameter stellt den Ordner &#8220;D:\/elevation\/data&#8221; (hier beispielhaft der Ordner auf dem Host mit unseren Daten) im Container unter &#8220;\/app\/data&#8221; zur Verf\u00fcgung. Da wir aber in aller Regel den Container nicht selbst starteten, sondern, die IDE das f\u00fcr uns erledigt, mussten wir den Parameter noch in der .csproj-Datei des Projekts mit<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">&lt;DockerfileRunArguments&gt;-v &quot;D:\/elevation\/data:\/app\/data:ro&quot;&lt;\/DockerfileRunArguments&gt;<\/code><\/pre>\n\n\n\n<p>festlegen.<\/p>\n\n\n\n<p><strong>ACHTUNG:<\/strong> Aufgrund der Dateigr\u00f6\u00dfe, sind die GeoTIFF-Dateien nicht im Repository enthalten und m\u00fcssen manuell heruntergeladen werden. Danach muss der Pfad zu den Dateien im Projekt angepasst werden, um es erfolgreich ausf\u00fchren zu k\u00f6nnen. Eine genauere Anleitung hierzu findet sich in der README-Datei im Repository des Backends.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"backend-unit-tests\">Backend-Unit Tests<\/h3>\n\n\n\n<p>Abschlie\u00dfend war das Backend noch zu testen. Dazu implementierten wir noch einige Unit Tests, sowohl f\u00fcr den Controller (hier wurde vor allem getestet, dass der Controller bei fehlerhaften Anfragen, entsprechende Fehlercodes zur\u00fcckgab), als auch f\u00fcr das Datenset (korrekte H\u00f6hendatenabfrage). Letzteres erwies sich aufgrund der Plattformproblematik erneut als z\u00e4her als gedacht. Um den GDAL-Port bei der Programmausf\u00fchrung zu initialisieren muss der Befehl<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">GdalBase.ConfigureAll();<\/code><\/pre>\n\n\n\n<p>einmal ausgef\u00fchrt werden. Dieser Befehl steht in unserer Anwendung in der Startup.cs-Datei, welche beim Anwendungsstart ausgef\u00fchrt wird. Da Unit Tests jedoch eine eigene Ausf\u00fchrungskonfiguration haben und die Startup.cs-Datei nicht ausf\u00fchren, wurde der GDAL-Port zun\u00e4chst nicht initialisiert. Dies hatte zur Folge, dass das Datenset nicht initialisiert wurde und wir somit st\u00e4ndig eine NullPointer-Exception bekamen. Dies ist unserer Meinung nach schlechte Codequalit\u00e4t des Ports, da es hier keine Fehlermeldung gab, sondern einfach NULL als Wert f\u00fcr den Datensatz zur\u00fcckgeben wurde ohne irgendeine Kontextinformation, die die Fehlersuche erleichtert h\u00e4tte. Aufgrund der mangelnden Dokumentation hat die L\u00f6sungsfindung auch hier deutlich zeitaufw\u00e4ndiger als n\u00f6tig. Letztlich fanden wir die L\u00f6sung, und f\u00fcgten den Initialisierungsbefehl in die Tests ein. Danach bekamen wir korrekte Ergebnisse. Als Unit Test-Framework verwendeten wir xUnit.net.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"zusammenfassung-der-hindernisse-und-learnings-aus-der-backend-entwicklung\">Zusammenfassung der Hindernisse und Erkenntnisse aus der Backend-Entwicklung<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Teilweise war etwas Einarbeitung in .NET Core n\u00f6tig, da nicht alle von uns bereits Erfahrung damit hatten<\/li>\n\n\n\n<li>Es musste einige Recherche betrieben werden, um eine geeignete Quelle f\u00fcr die H\u00f6hendaten zu finden<\/li>\n\n\n\n<li>Das Verst\u00e4ndnis der verschiedenen Koordinatenprojektionssysteme und deren Umrechnung war etwas kompliziert<\/li>\n\n\n\n<li>Wir mussten einen Weg finden, die gro\u00dfen GeoTIFF-Dateien im Container bereitzustellen<\/li>\n\n\n\n<li><strong>Aber mit Abstand am wichtigsten:<\/strong> wir haben gelernt, dass die Auswahl der Plattform nicht nur von architektonischen Gesichtspunkten oder gar denen der Bequemlichkeit abh\u00e4ngen sollte, sondern dass auch die Anwendungsdom\u00e4ne eine entscheidende Rolle spielen sollte. Im Bereich Geo-Daten-Processing ist <strong>Python<\/strong> die mit Abstand am meisten verwendete Sprache, sodass die meisten Bibliotheken und Communityinhalte sich auf diese Sprache beziehen. Es ist also dringend zu empfehlen, f\u00fcr solche Anwendungstypen Python zu verwenden. Anderenfalls bleibt nur die M\u00f6glichkeit, sich &#8211; wie wir &#8211; auf (oft unvollst\u00e4ndige und schlecht dokumentierte) kleinere Bibliotheken oder Ports zu verlassen, was die Entwicklung stark verkompliziert.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"den-server-stressen\">Den Server stressen<\/h2>\n\n\n\n<p>Wir kommen zum n\u00e4chsten Puzzleteil des Projektes, dem Stresser.<\/p>\n\n\n\n<p>Um erkennen zu k\u00f6nnen, welche Auswirkungen eine hohe Anzahl von Anfragen auf das System hat und wie wir dagegen vorgehen k\u00f6nnen, m\u00fcssen wir zuerst das System mit einer hohen Anzahl an Abfragen stressen. Man k\u00f6nnte hier fast von einer DDoS-Attacke sprechen. Allerdings m\u00fcssen wir nicht ganz so weit gehen und ein ganzes Botnetz f\u00fcr diesen Test zu erschaffen, das w\u00e4re dann doch etwas zu viel des guten. Welche Alternativen haben wir? Es geht darum, eine reproduzierbare Testumgebung f\u00fcr das Backend zu schaffen, um in einem realistischen Umfeld erkunden zu k\u00f6nnen, wie sich die Elevation API unter Last verh\u00e4lt. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"anforderungen\">Anforderungen<\/h3>\n\n\n\n<p>Wir wollen nat\u00fcrlich nicht einfach mit irgendeiner beliebigen Sprache ein Programm schreiben, das in einem for-Loop sequenziell einige HTTP-Requests versendet. Wir wollen m\u00f6glichst schnell und unkompliziert unsere API mit hunderten oder auch hunderttausenden Anfragen m\u00f6glichst parallel stressen. Wir wollen m\u00f6glichst keinen Overhead. Einfach ein kleines Programm, das keine Framework-Infrastruktur um sich ben\u00f6tigt, um zu existieren. Nat\u00fcrlich wollen wir das Ganze konfigurieren k\u00f6nnen: Welche URL soll gestresst werden, wie h\u00e4ufig, mit welcher HTTP-Methode und welchem Body Inhalt. Am besten w\u00e4re dazu ein Web-Interface, um nicht bei jedem Ausf\u00fchren als erstes &#8211;help tippen zu m\u00fcssen.<\/p>\n\n\n\n<p>Also sammeln wir mal die Anforderungen:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Nebenl\u00e4ufigkeit<\/li>\n\n\n\n<li>Leichtgewichtig<\/li>\n\n\n\n<li>Schnell<\/li>\n\n\n\n<li>Einfach zu deployen<\/li>\n\n\n\n<li>Web-Interface<\/li>\n<\/ol>\n\n\n\n<p>Bevor wir anfangen den Stresser zu bauen, m\u00fcssen wir entscheiden, welche Sprache wir w\u00e4hlen. In den Topf geworfen wurde .NET. Zwar bietet das Framework gute Performance, hat mir jedoch als Framework schon zu viel Overhead. Dazu kommt, dass wir keinerlei Erfahrung damit haben. JavaScript\/Node, war die zweite Option und bringt einiges mit, was hier hilfreich ist. Allerdings ist es nicht gerade f\u00fcr seine Geschwindigkeit oder Nebenl\u00e4ufigkeit bekannt. Entschieden haben wir uns letztendlich f\u00fcr GoLang. Die Idee kam daher, dass wir vor Jahren schon einmal in der Sprache entwickelt habe, aber seither keine Ber\u00fchrung mehr damit hatte. Was f\u00fcr Go spricht, ist, dass es mit Fokus auf Nebenl\u00e4ufigkeit entwickelt wurde. Die Standard-Bibliothek bringt alles mit, was wir ben\u00f6tigen. Au\u00dferdem kompilieren Go Programme zu einer einzigen kleinen Executable, in der alle Abh\u00e4ngigkeiten enthalten sind.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Zeichnung-2.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"259\" data-attachment-id=\"22047\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/zeichnung-2\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Zeichnung-2.png\" data-orig-size=\"1305,330\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Zeichnung-2\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Zeichnung-2-1024x259.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Zeichnung-2-1024x259.png\" alt=\"\" class=\"wp-image-22047\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Zeichnung-2-1024x259.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Zeichnung-2-300x76.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Zeichnung-2-768x194.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Zeichnung-2.png 1305w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>Also let&#8217;s Go.  <\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"umsetzung\">Umsetzung<\/h3>\n\n\n\n<p>Der Stresser besteht aus drei Komponenten:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Web-Frontend<br>Um den Stresser zu konfigurieren, ben\u00f6tigen wir ein Web-Interface. Zwar ist es m\u00f6glich, das mittels <a href=\"https:\/\/go.dev\/doc\/articles\/wiki\/\">Web-Templates<\/a> direkt in Go umzusetzen, aber was soll der Geiz. Wir erstellen das Web-Frontend mit React. Das ist zwar absolut Overkill, aber es geht nur darum m\u00f6glichst schnell und unkompliziert eine Weboberfl\u00e4che zu erstellen. In dieser Web-Oberfl\u00e4che k\u00f6nnen wir dann die definierten Parameter konfigurieren, den Stresser starten und das Resultat einsehen.<\/li>\n\n\n\n<li>Webserver<br>Das mit React erstellte Frontend liefern wir mit einem Go Fileserver als statische Dateien aus. Dieser Go-Server fungiert au\u00dferdem als Proxy und startet den Stresser.<\/li>\n\n\n\n<li>Der Stresser selbst<br>Um den Stresser auch ohne Overhead verwenden zu k\u00f6nnen, wird er als zus\u00e4tzliches Go-Programm geschrieben, welches alle ben\u00f6tigten Parameter \u00fcber die Kommandozeile bekommt. So kann er einerseits \u00fcber das Web-Frontend gestartet werden, allerdings auch einfach \u00fcber die Kommandozeile, falls ben\u00f6tigt.<br><\/li>\n<\/ol>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large is-resized\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/stresser-frontend.png\"><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"22053\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/stresser-frontend\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/stresser-frontend.png\" data-orig-size=\"1034,840\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"stresser-frontend\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/stresser-frontend-1024x832.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/stresser-frontend-1024x832.png\" alt=\"\" class=\"wp-image-22053\" width=\"507\" height=\"411\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/stresser-frontend-1024x832.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/stresser-frontend-300x244.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/stresser-frontend-768x624.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/stresser-frontend.png 1034w\" sizes=\"auto, (max-width: 507px) 100vw, 507px\" \/><\/a><figcaption class=\"wp-element-caption\">Web-Frontend des Stresser<\/figcaption><\/figure>\n\n\n\n<p>Es gibt ein paar verschiedene M\u00f6glichkeiten, den Stresser in Go umzusetzen. Um herauszufinden, welche nun die Beste ist, haben wir verschiedene M\u00f6glichkeiten implementiert, die Performance gemessen (Zeit ben\u00f6tigt, um X Anfragen zu verschicken) und verglichen.<\/p>\n\n\n\n<p>Die wesentlichen drei M\u00f6glichkeiten sind:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Inkrementell<\/li>\n\n\n\n<li>Worker\n<ul class=\"wp-block-list\">\n<li>Mit Waitgroup<\/li>\n\n\n\n<li>Mit Message Queues<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p>Um die Effizienz der verschiedenen M\u00f6glichkeiten zu messen, wurden zwei Metriken gemessen:<\/p>\n\n\n\n<ul class=\"wp-block-list\" id=\"block-44e87e03-d496-4496-9269-7294a2499c8b\">\n<li>Ben\u00f6tigte Zeit von erster bis letzter Anfrage<\/li>\n\n\n\n<li>Verh\u00e4ltnis Fehlschl\u00e4ge \/ erfolgreiche Anfragen<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"inkrementell\">Inkrementell<\/h4>\n\n\n\n<p>Das ganze Inkrementell auszuf\u00fchren d\u00fcrfte die einfachste M\u00f6glichkeit sein, ist aber wie man sich denken kann auch die ineffizienteste. Hierzu werden in einer Schleife eine Anfrage nach der n\u00e4chsten gesendet. Die Frage, ob es Performance-Unterschiede zwischen For-\/While-\/ oder anderen Schleifen gibt, ist obsolet, da Go nur eine Variante anbietet.<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-stripes\"><table><thead><tr><th>Metrik<\/th><th>100<\/th><th>1.000<\/th><th>10.000<\/th><\/tr><\/thead><tbody><tr><td>Zeit (in Sekunden)<\/td><td>0,0895689<\/td><td>0,8537029<\/td><td>8,4739672<\/td><\/tr><tr><td>Fehlschl\u00e4ge\/Erfolge<\/td><td>0 \/ 100<\/td><td>0 \/ 1000<\/td><td>0 \/ 9817<\/td><\/tr><\/tbody><\/table><figcaption class=\"wp-element-caption\">Testserver und Stresser auf selben System;  Werte \u00fcber 10 Versuche gemittelt<\/figcaption><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"workerpool\">Workerpool&#8230;<\/h4>\n\n\n\n<p>Eine interessantere Methode unser Ziel zu erreichen ist die Umsetzung mit &#8220;Workern&#8221;. Ein Worker in Go ist eigentlich nichts anderes als eine Methode, welche in einer separaten Go-Routine gestartet wird. Eine Go-Routine ist ein leichtgewichtiger Thread, der mit dem Befehl &#8220;go&#8221; gestartet wird.<\/p>\n\n\n\n<p>Eine Go-Routine ist nicht zu verwechseln mit einem normalen Thread, denn Go verwendet einen eigenen Scheduler, welcher mehrere Go-Routines \u00fcber einen Thread verteilt. Die genaue Funktionsweise des Go-Routine Schedulers ist im <a href=\"https:\/\/go.dev\/src\/runtime\/proc.go\" title=\"Quellcode \">Quellcode <\/a>selbst umfangreich beschrieben und von <a href=\"https:\/\/medium.com\/a-journey-with-go\/go-goroutine-os-thread-and-cpu-management-2f5a5eaf518a\" title=\"Vincent Blanchon\">Vincent Blanchon<\/a> sehr gut aufbereitet.<\/p>\n\n\n\n<p>Die drei Hauptprinzipien des Schedulers sind laut Quellcode:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">\n\/\/ G - goroutine.\n\/\/ M - worker thread, or machine.\n\/\/ P - processor, a resource that is required to execute Go code.\n\/\/     M must have an associated P to execute Go code, however it can be\n\/\/     blocked or in a syscall w\/o an associated P.<\/code><\/pre>\n\n\n\n<p>F\u00fchren wir nun mehrere HTTP-Requests gleichzeitig aus (also in verschiedenen Go-Routines) ist, sieht der Ablauf z.B. wie folgt aus:<\/p>\n\n\n\n<p>Zuerst wird einer der erstellten Worker (Go-Routine) vom Scheduler gestartet. Dieser Worker f\u00fchrt das http-Request aus. Dieser Worker wartet anschlie\u00dfend auf die Antwort und wird so lange vom Scheduler im Network Poller &#8220;geparkt&#8221;. <\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Untitled-Diagram-Page-2.drawio.png\"><img loading=\"lazy\" decoding=\"async\" width=\"481\" height=\"281\" data-attachment-id=\"22070\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/untitled-diagram-page-2-drawio\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Untitled-Diagram-Page-2.drawio.png\" data-orig-size=\"481,281\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Untitled-Diagram-Page-2.drawio\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Untitled-Diagram-Page-2.drawio.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Untitled-Diagram-Page-2.drawio.png\" alt=\"\" class=\"wp-image-22070\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Untitled-Diagram-Page-2.drawio.png 481w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Untitled-Diagram-Page-2.drawio-300x175.png 300w\" sizes=\"auto, (max-width: 481px) 100vw, 481px\" \/><\/a><\/figure>\n\n\n\n<p>Nun wird der n\u00e4chste wartende Worker vom Scheduler ausgew\u00e4hlt und der Prozess wiederholt sich.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Untitled-Diagram-Page-3.drawio.png\"><img loading=\"lazy\" decoding=\"async\" width=\"481\" height=\"286\" data-attachment-id=\"22071\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/untitled-diagram-page-3-drawio\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Untitled-Diagram-Page-3.drawio.png\" data-orig-size=\"481,286\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Untitled-Diagram-Page-3.drawio\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Untitled-Diagram-Page-3.drawio.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Untitled-Diagram-Page-3.drawio.png\" alt=\"\" class=\"wp-image-22071\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Untitled-Diagram-Page-3.drawio.png 481w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Untitled-Diagram-Page-3.drawio-300x178.png 300w\" sizes=\"auto, (max-width: 481px) 100vw, 481px\" \/><\/a><\/figure>\n\n\n\n<p>Wenn mittlerweile eine Antwort eingetroffen ist, erf\u00e4hrt der Scheduler das vom Network Poller und f\u00fchrt den betreffenden Worker weiter aus.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Untitled-Diagram-Page-4.drawio.png\"><img loading=\"lazy\" decoding=\"async\" width=\"481\" height=\"96\" data-attachment-id=\"22072\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/untitled-diagram-page-4-drawio\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Untitled-Diagram-Page-4.drawio.png\" data-orig-size=\"481,96\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Untitled-Diagram-Page-4.drawio\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Untitled-Diagram-Page-4.drawio.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Untitled-Diagram-Page-4.drawio.png\" alt=\"\" class=\"wp-image-22072\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Untitled-Diagram-Page-4.drawio.png 481w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Untitled-Diagram-Page-4.drawio-300x60.png 300w\" sizes=\"auto, (max-width: 481px) 100vw, 481px\" \/><\/a><\/figure>\n\n\n\n<p>Nat\u00fcrlich haben wir uns f\u00fcr eine Umsetzung mit Workern entschieden. Hierbei gibt es grunds\u00e4tzlich zwei M\u00f6glichkeiten der Umsetzung. Dabei geht es vor allem darum, wie die Worker erstellt werden und wie mit der Beendigung umgegangen wird. Wir werden nicht im Detail auf die Funktionsweise eingehen, aber werde versuchen die Grundlagen und Unterschiede klarzumachen.<\/p>\n\n\n\n<h5 class=\"wp-block-heading\" id=\"mit-waitgroup\">&#8230; mit Waitgroup<\/h5>\n\n\n\n<p>Bei der Verwendung von Waitgroups, werden die Worker inkrementell erstellt und der Waitgroup hinzugef\u00fcgt. Demnach werden insgesamt so viele Worker erstellt, wie Anfragen gesendet werden sollen. Ist der Worker beendet, wird er aus der Waitgroup entfernt. Der Stresser ist fertig, wenn die Waitgroup leer ist. <\/p>\n\n\n\n<p>Lokal getestet ergeben sich folgende Werte: <\/p>\n\n\n\n<figure class=\"wp-block-table is-style-stripes\"><table><thead><tr><th>Metrik<\/th><th>100<\/th><th>1.000<\/th><th>10.000<\/th><\/tr><\/thead><tbody><tr><td>Zeit (in Sekunden)<\/td><td>0,022555<\/td><td>0,706024<\/td><td>2,679127<\/td><\/tr><tr><td>Fehlschl\u00e4ge\/Erfolge<\/td><td>0 \/ 100<\/td><td>0 \/ 1000<\/td><td>183 \/ 9817<\/td><\/tr><\/tbody><\/table><figcaption class=\"wp-element-caption\">Testserver und Stresser auf selben System;  Werte \u00fcber 10 Versuche gemittelt; Anzahl Worker = Anzahl Anfrage<\/figcaption><\/figure>\n\n\n\n<p>Ein Problem mit dieser Methode ist, dass bei einer hohen Anzahl Anfragen Memory-Fehler auftreten, da zu viele Worker gestartet werden. Man sieht, dass im Fall von 10.000 Anfragen, 183 Anfragen fehlgeschlagen sind. Dabei handelt es sich um clientseitige Fehler und nicht um Antworten mit einem Code != 2xx. Diese Zahl schwankt stark und h\u00e4ngt davon ab, wie schnell Anfragen beantwortet werden. Werden sie zu langsam beantwortet, laufen zu viele Worker gleichzeitig.<\/p>\n\n\n\n<h5 class=\"wp-block-heading\" id=\"mit-channels\">&#8230; mit Channels<\/h5>\n\n\n\n<p>\u00dcber Channels k\u00f6nnen Werte gesendet werden. An einer Stelle werden Werte eingegeben, ein Worker kann an einer anderen Stelle einen Wert auslesen. Wir bef\u00fcllen also einen Channel mit X Werten (X = Anzahl an Anfragen) und die Worker arbeiten diese nach und nach ab. Der Vorteil ist, dass einfach kontrolliert werden kann, wie viele Worker wir verwenden und diese k\u00f6nnen alle vor dem eigentlichen Start initialisiert werden.<\/p>\n\n\n\n<p>Lokal getestet ergeben sich folgende Werte: <\/p>\n\n\n\n<figure class=\"wp-block-table is-style-stripes\"><table><thead><tr><th>Metrik<\/th><th>100<\/th><th>1.000<\/th><th>10.000<\/th><\/tr><\/thead><tbody><tr><td>Zeit (in Sekunden)<\/td><td>0,0224859<\/td><td>0,6068043<\/td><td>2,2542878<\/td><\/tr><tr><td>Fehlschl\u00e4ge\/Erfolge<\/td><td>0 \/ 100<\/td><td>0 \/ 1000<\/td><td>0 \/ 9817<\/td><\/tr><\/tbody><\/table><figcaption class=\"wp-element-caption\">Testserver und Stresser auf selben System;  Werte \u00fcber 10 Versuche gemittelt; Anzahl Worker = Anzahl Anfrage<\/figcaption><\/figure>\n\n\n\n<p>Waitgroups sind grunds\u00e4tzlich performanter als Channels (siehe <a href=\"https:\/\/stackoverflow.com\/a\/17868720\/16190467\">Stackoverflow<\/a>), beziehungsweise wird eine Mischung aus beiden empfohlen. Channels bieten uns einige Vorteile, die uns mehr Kontrolle bieten. Und wie die Performance-Tests zeigen, unterscheiden sich beide Technologien in unserem Anwendungsfall kaum.<\/p>\n\n\n\n<p><strong>Wie viele Worker sollte man verwenden? <\/strong><\/p>\n\n\n\n<p>Man kann nicht pauschal sagen, ob sich mehr Worker als verf\u00fcgbare Threads nicht lohnen, da diese, wie oben gezeigt, parallel verarbeitet werden k\u00f6nnen. Allerdings funktioniert es auch nicht nach dem Motto &#8220;viel hilft viel&#8221;, da der Stresser irgendwann mehr mit Scheduling als ausf\u00fchren besch\u00e4ftigt ist. Es l\u00e4sst sich leider nicht so leicht eine optimale Anzahl an Workern benennen (z.B. verf\u00fcgbare Threads * 4 o.\u00c4.). Ein Punkt, auf den man beispielsweise relativ wenig Einfluss hat, ist die Antwortgeschwindigkeit des Servers, die durch die Bearbeitungsdauer, Netzwerkverbindung etc. beeinflusst wird. M\u00fcssen Worker lange im Network Poller warten, k\u00f6nnen sie auch nicht wiederverwendet werden. Aus diesem Grund haben wir die Anzahl der Worker als zus\u00e4tzliches Konfigurationsfeld hinzugef\u00fcgt.<\/p>\n\n\n\n<p>Zur Veranschaulichung hier ein Vergleich. Hierf\u00fcr wurde auf dem Testserver eine Last simuliert (1 Sekunde Timeout). Nun wurden mit unterschiedlich vielen Workern 1000 Anfragen gesendet.<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-stripes\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Worker<\/strong><\/td><td>8<\/td><td>16<\/td><td>32<\/td><td>100<\/td><td>500<\/td><td>1000<\/td><\/tr><tr><td><strong>Dauer<\/strong><\/td><td>2min 1,14s<\/td><td>1min 3,65s<\/td><td>32,42s<\/td><td>10,17s<\/td><td>2,45s<\/td><td>1,63s<\/td><\/tr><\/tbody><\/table><figcaption class=\"wp-element-caption\">Vergleich der Dauer zum Versenden von 1000 Anfragen an einen Testserver<\/figcaption><\/figure>\n\n\n\n<p>Das Ergebnis best\u00e4tigt die Vermutung von vorher. Das Stressen dauert mit 1000 Workern am k\u00fcrzesten, da f\u00fcr alle Anfragen ein extra Worker existiert und nicht auf einen im Network Poller gewartet werden muss.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"zusammenfassung-der-hindernisse-und-learnings-der-stresser-entwicklung\">Zusammenfassung der Hindernisse und Learnings der Stresser-Entwicklung<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Zun\u00e4chst nat\u00fcrlich die Wahl der richtigen Entwicklungstools und die damit verbundene Anforderungsanalyse<\/li>\n\n\n\n<li>Da unsere Expertise in Go kaum vorhanden war, war das erneute einlernen in eine deutlich andere Sprache, als JavaScript eine kleine Herausforderung.<br>Damit verbunden und am wichtigsten, war das Ausprobieren und Vergleichen verschiedener M\u00f6glichkeiten den Server zu Stressen.<\/li>\n\n\n\n<li>Gerade bei der Evaluierung der verschiedenen Stresser-Varianten, war es wichtig, das interne Scheduling von Go zu verstehen. Dabei haben wir auch gelernt, das die Go-Community wohl noch recht klein und jung ist. Denn wo man f\u00fcr andere Sprachen Millionen Antworten findet, muss man hier teilweise noch sehr stark recherchieren.<\/li>\n\n\n\n<li>Eine &#8220;Honorable Mention&#8221; ist auf jeden Fall das Containerizen des ganzen und Generelle bereitstellen der Anwendung. Zwar wurde in diesem Blogeintrag recht wenig darauf eingegangen, da es den Kontext gesprengt h\u00e4tte, jedoch war es sehr interessant, zu erkunden welche M\u00f6glichkeiten es gibt, die verschiedenen Bauteile des Stressers (Frontend, Proxy-Server, Stresser) zusammenzubringen.<\/li>\n\n\n\n<li>Und last but not least: Auch in diesem Fall wieder Monitoring. Welche Informationen brauchen wir, wie werden sie bereitgestellt, etc. <\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"monitoring\">Monitoring<\/h2>\n\n\n\n<p>In diesem Kapitel werden wir uns einzele Monitoring Tools genauer anschauen. Anschlie\u00dfend werden wir einen Versuchsaufbau gestalten, womit wir einen Benchmark gegen die Elevation API durchf\u00fchren werden. Wir diskutieren die Ergebnisse und versuchen anschlie\u00dfend die Frage zu beantworten, was uns das Tracing und Logging an Performance kostet. Die Learnings aus diesem Kapitel werden am Ende zusammengefasst.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Ziele<\/h3>\n\n\n\n<p>Da wir bisher kaum Erfahrung im Bereich Monitoring haben, war es also das erste Ziel, Tools f\u00fcr das Monitoring auszuw\u00e4hlen und diese in Betrieb zu nehmen. In der Vorlesung kam das Thema Tracing auf, welches uns besonders zugesagt hat und wir es deshalb ausprobieren wollten. Dar\u00fcber hinaus musste die Elevation API erst einmal in der Lage sein, Metriken f\u00fcr das Monitoring zu bereitzustellen.<\/p>\n\n\n\n<p>Im n\u00e4chsten Schritt soll ein Versuchsaufbau zur Bestimmung der Leistungsf\u00e4higkeit und Effizienz unseres Backends geplant und durchgef\u00fchrt werden. Dabei soll der bereits vorgestellte Stresser die Last f\u00fcr das Backend erzeugen. Da das Monitoring eine Tracing-Komponente enthalten wird, gilt es zudem herauszufinden, was das Tracing in Hinblick auf die Performance in unserem Fall kostet.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Die Monitoring Komponenten<\/h3>\n\n\n\n<p>Nach einer Recherche haben wir uns f\u00fcr folgende Monitoring Komponenten entschieden:<br>Prometheus, node_exporter, cAdvisor, Grafana und Jaeger. Diese Tools werden in den n\u00e4chsten Abschnitten n\u00e4her vorgestellt.<\/p>\n\n\n\n<p>Im Bereich Monitoring sind wir bei der Recherche auf weitere Projekte wie <a href=\"https:\/\/github.com\/zabbix\/zabbix\" title=\"zabbix\">zabbix<\/a>, <a href=\"https:\/\/github.com\/NagiosEnterprises\/nagioscore\" title=\"nagios\">nagios<\/a> und <a href=\"https:\/\/github.com\/Icinga\/icinga2\" title=\"icinga2\">icinga2<\/a> gesto\u00dfen. Letzteres konnten wir bereits in der Veranstaltung <a href=\"https:\/\/www.hdm-stuttgart.de\/studieninhalte\/block?sgname=Medieninformatik+%28Bachelor%2C+7+Semester%29&amp;sgblockID=2572112&amp;sgang=550033&amp;blockname=Software+Defined+Infrastructure\" title=\"&quot;Software Defined Infrastructure&quot;\">&#8220;Software Defined Infrastructure&#8221;<\/a> kennenlernen. Wir haben uns f\u00fcr <a href=\"https:\/\/github.com\/prometheus\/prometheus\" title=\"Prometheus\">Prometheus<\/a> entschieden, da es uns am etabliertesten vorkam und uns die Idee hinter den Exportern gefiel.<\/p>\n\n\n\n<p>Im Bereich Tracing sind wir neben <a href=\"https:\/\/github.com\/jaegertracing\" title=\"Jaeger \">Jaeger<\/a> auch auf <a href=\"https:\/\/kamon.io\/apm\/pricing\/\" title=\"Kamon\">Kamon<\/a>, <a href=\"https:\/\/sentry.io\/pricing\/\" title=\"Sentry \">Sentry <\/a>und <a href=\"https:\/\/www.datadoghq.com\/\" title=\"DataDog \">Datadog <\/a>gesto\u00dfen. Wir haben uns f\u00fcr Jaeger entschieden, da die anderen L\u00f6sungen nur in Form von Cloud-Plattformen zur Verf\u00fcgung stehen und entweder sehr eingeschr\u00e4nkte Features oder nur kurze kostenlose Testversionen anbieten.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Prometheus<\/h4>\n\n\n\n<p><a href=\"https:\/\/github.com\/prometheus\/prometheus\" title=\"Prometheus \">Prometheus <\/a>ist ein Monitoring Tool welches mit einer Zeitreihendatenbank arbeitet. Es ist somit auf das Speichern von Zeitreihen wie Sensordaten ausgelegt. Mithilfe der <a href=\"https:\/\/prometheus.io\/docs\/prometheus\/latest\/querying\/basics\/\" title=\"PromQL\">PromQL<\/a> k\u00f6nnen Daten gezielt ausgew\u00e4hlt und aggregiert werden.<\/p>\n\n\n\n<p>Mithilfe sog. <a href=\"https:\/\/prometheus.io\/docs\/instrumenting\/exporters\/\" title=\"Exportern\">Exportern<\/a> werden Metriken bereitgestellt, die von Prometheus in regelm\u00e4\u00dfigen, selbst definierbaren Zeitabst\u00e4nden eingesammelt werden. Es gibt dabei fertige Exporter wie node_exporter. Zus\u00e4tzlich k\u00f6nnen eigene Exporter geschrieben werden.<\/p>\n\n\n\n<p>F\u00fcr die Elevation API stellen wir mithilfe der <a href=\"https:\/\/github.com\/prometheus-net\/prometheus-net\" title=\"prometheus-net.AspNetCore\">prometheus-net.AspNetCore<\/a> Bibliothek unsere eigene Metriken bereit. Die Installation und der Quick Start der Bibliothek funktionierte auf Anhieb. Neben verschiedenen Versuchen sinnvolle Metriken zu finden, zeigen wir euch hier die Implementation eines einfachen Counters, der jedes Mal um eins hochz\u00e4hlt, wenn die H\u00f6henmeter einer Koordinaten bestimmt wurden:<\/p>\n\n\n\n<!-- Prometheus Counter Implementierung Beispiel  -->\n<!-- https:\/\/gist.github.com\/Morodar\/0eb1ab352c559b81d810822f5c5368fd -->\n<script src=\"https:\/\/gist.github.com\/Morodar\/0eb1ab352c559b81d810822f5c5368fd.js\"><\/script>\n\n\n\n<p>So banal diese Metrik scheinen mag, so ist diese Metrik f\u00fcr die Bestimmung der Performance im Lasttest essenziell. Im sp\u00e4teren Verlauf k\u00f6nnen wir mit der erw\u00e4hnten <a href=\"https:\/\/prometheus.io\/docs\/prometheus\/latest\/querying\/basics\/\" title=\"PromQL\">PromQL<\/a> eine Abfrage formulieren, die uns die Anzahl der berechneten H\u00f6henmetern der letzten Minute ausgibt.<\/p>\n\n\n\n<p>Der folgende Code Block zeigt die Metriken, die von der Elevation API erzeugt werden und \u00fcber &#8220;\/metrics&#8221; abgefragt werden k\u00f6nnen:<\/p>\n\n\n\n<!-- Prometheus Metrics Example -->\n<!-- https:\/\/gist.github.com\/Morodar\/ff9fc36c24da461a061e45be3ac50349 -->\n<script src=\"https:\/\/gist.github.com\/Morodar\/ff9fc36c24da461a061e45be3ac50349.js\"><\/script>\n\n\n\n<p>Somit w\u00e4re der Exporter der Elevation API schon mal bereit.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">node_exporter<\/h4>\n\n\n\n<p>Der Exporter <a href=\"https:\/\/github.com\/prometheus\/node_exporter\" title=\"node_exporter\">node_exporter<\/a> wurde vom Prometheus-Team entwickelt und stellt Hardware und OS Metriken des Systems bereit. Dadurch lassen sich neben CPU-, Netzwerk-Last und RAM-Belegung, auch die Lese-\/Schreib-Raten der einzelnen Laufwerke beobachten.<\/p>\n\n\n\n<p><em>(node_exporter bietet noch viele weitere detailreiche Metriken zu CPU, Netzwerk, RAM und zum Dateisystem an. Einige erfordern Recherche, um sie \u00fcberhaupt zu verstehen. Es gibt einige Metriken wie &#8220;node_hwmon_temp_celsius&#8221;, die nice-to-have sind, wir aber f\u00fcr das Benchmarking eine untergeordnete Rolle spielen)<\/em><\/p>\n\n\n\n<p>Die folgenden beide Bilder zeigen Diagramme, die ein paar der genannten Metriken aus node_exporter visualisieren:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-1.png\"><img loading=\"lazy\" decoding=\"async\" width=\"863\" height=\"323\" data-attachment-id=\"22241\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/image-1-7\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-1.png\" data-orig-size=\"863,323\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"image-1\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-1.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-1.png\" alt=\"\" class=\"wp-image-22241\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-1.png 863w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-1-300x112.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-1-768x287.png 768w\" sizes=\"auto, (max-width: 863px) 100vw, 863px\" \/><\/a><figcaption class=\"wp-element-caption\">node_exporter CPU und RAM Last<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-2.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"304\" data-attachment-id=\"22242\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/image-2-8\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-2.png\" data-orig-size=\"1040,309\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"image-2\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-2-1024x304.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-2-1024x304.png\" alt=\"\" class=\"wp-image-22242\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-2-1024x304.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-2-300x89.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-2-768x228.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-2.png 1040w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">node_exporter RAM und SSD-Leserate<\/figcaption><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\">cAdvisor<\/h4>\n\n\n\n<p>Da node_exporter &#8220;nur&#8221; Metriken des Host-Systems bereitstellt, setzen wir zus\u00e4tzlich <a href=\"https:\/\/github.com\/google\/cadvisor\" title=\"cAdvisor \">cAdvisor <\/a>ein, welches von Google entwickelt wurde. cAdvisor (Container Advisor) ist \u00e4hnlich wie node_exporter, mit dem Unterschied, dass es allgemeine Metriken \u00fcber Docker-Container sammelt.<\/p>\n\n\n\n<p>Die folgenden beide Bilder zeigen Diagramme, die ein paar Metriken aus cAdvisor visualisieren:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-4.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"278\" data-attachment-id=\"22244\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/image-4-7\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-4.png\" data-orig-size=\"1137,309\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"image-4\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-4-1024x278.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-4-1024x278.png\" alt=\"\" class=\"wp-image-22244\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-4-1024x278.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-4-300x82.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-4-768x209.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-4.png 1137w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">CPU Last und Anzahl der Threads der einzelnen Container<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-5.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"274\" data-attachment-id=\"22245\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/image-5-6\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-5.png\" data-orig-size=\"1133,303\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"image-5\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-5-1024x274.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-5-1024x274.png\" alt=\"\" class=\"wp-image-22245\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-5-1024x274.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-5-300x80.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-5-768x205.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-5.png 1133w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">RAM Nutzung und gesendeter Netwerkverkehr<\/figcaption><\/figure>\n\n\n\n<p>Dank node_exporter und cAdvisor haben wir nun grundlegende Metriken unseres Systems im Blick. Allerdings hat uns cAdvisor auch gezeigt, dass es standardm\u00e4\u00dfig doch sehr Ressourcenhungrig ist:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large is-resized\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-10.png\"><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"22256\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/image-10-4\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-10.png\" data-orig-size=\"1153,519\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"image-10\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-10-1024x461.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-10-1024x461.png\" alt=\"\" class=\"wp-image-22256\" width=\"522\" height=\"235\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-10-1024x461.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-10-300x135.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-10-768x346.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-10.png 1153w\" sizes=\"auto, (max-width: 522px) 100vw, 522px\" \/><\/a><figcaption class=\"wp-element-caption\">CPU Auslastung der Container im Idle<\/figcaption><\/figure>\n\n\n\n<p>cAdvisor hat in unserem Versuchsaufbau im Idle direkt 10% CPU Last &#8220;verschlungen&#8221;. Das fanden wir etwas viel, weshalb wir nach einer L\u00f6sung gesucht und gefunden haben. Wie bereits erw\u00e4hnt bietet node_exporter viele weitere Metriken an. Das gleiche gilt standardm\u00e4\u00dfig auch f\u00fcr cAdvisor. Folglich konnten wir durch eine Anpassung des Startbefehls cAdvisor dazu bringen, unn\u00f6tige Metriken nicht zu sammeln und sich auf Docker Metriken zu beschr\u00e4nken. Der folgende Code-Block zeigt unsere cAdvisor Konfiguration:<\/p>\n\n\n\n<!-- cAdvisor yml config -->\n<!-- https:\/\/gist.github.com\/Morodar\/c60e1e821d3370c4172032fed8ecced3 -->\n<script src=\"https:\/\/gist.github.com\/Morodar\/c60e1e821d3370c4172032fed8ecced3.js\"><\/script>\n\n\n\n<p>Die \u00c4nderung hat gezeigt, dass cAdvisor statt ~10% nun ~2% CPU-Last ben\u00f6tigt. Daran zeigt sich, dass man standardm\u00e4\u00dfige Konfigurationen immer hinterfragen sollte.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/cAdvisor-fixed.png\"><img loading=\"lazy\" decoding=\"async\" width=\"653\" height=\"296\" data-attachment-id=\"22247\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/cadvisor-fixed\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/cAdvisor-fixed.png\" data-orig-size=\"653,296\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"cAdvisor-fixed\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/cAdvisor-fixed.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/cAdvisor-fixed.png\" alt=\"\" class=\"wp-image-22247\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/cAdvisor-fixed.png 653w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/cAdvisor-fixed-300x136.png 300w\" sizes=\"auto, (max-width: 653px) 100vw, 653px\" \/><\/a><figcaption class=\"wp-element-caption\">CPU Auslastung der Container im Idle (mit angepasster cAdvisor Konfiguration)<\/figcaption><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\">Grafana<\/h4>\n\n\n\n<p><a href=\"https:\/\/github.com\/grafana\/grafana\" title=\"Grafana \">Grafana <\/a>erlaubt die Visualisierung von Metriken aus diversen Quellen wie Prometheus, Jaeger und noch vielen weiteren. Dar\u00fcber hinaus k\u00f6nnen Regeln definiert werden, die unter bestimmten F\u00e4llen Alarme ausl\u00f6sen und einen benachrichtigen, wenn z.B. ein Service f\u00fcr eine gewisse Zeit nicht erreichbar ist oder auff\u00e4llig hohe Lasten f\u00fcr l\u00e4ngere Zeit beobachtet werden. <\/p>\n\n\n\n<p>Die bisherigen gezeigten Diagramme stammen alle aus eigens konfigurierten Grafana Dashboards. Als Beispiel zeigen wir, wie wir das Diagramm zur Anzeige der berechneten H\u00f6henmeter pro Minute erstellt haben.<\/p>\n\n\n\n<p>Dazu erstellen wir ein neues Panel und w\u00e4hlen als Datenquelle &#8220;Prometheus&#8221; aus, welches wir bereits \u00fcber <a href=\"https:\/\/grafana.com\/docs\/grafana\/latest\/administration\/provisioning\/\" title=\"Provisioning Config Files\">Provisioning Config Files<\/a> konfiguriert haben. Anschlie\u00dfend finden wir \u00fcber den Metric Browser unsere eigene Metric &#8220;elevation_getAltitudeAt_count&#8221;, welcher nur ein einfacher Z\u00e4hler ist. Da Prometheus verschiedene <a href=\"https:\/\/prometheus.io\/docs\/prometheus\/latest\/querying\/functions\/#rate\" title=\"Query Functions\">Query Functions<\/a> anbietet, k\u00f6nnen wir \u00fcber die <a href=\"https:\/\/prometheus.io\/docs\/prometheus\/latest\/querying\/basics\/\" title=\"PromQL\">PromQL<\/a> mithilfe der rate() den durchschnittlichen Anstieg eines Wertes einer bestimmten Zeitspanne ermitteln. <br>Mit &#8220;[1m]&#8221; m\u00f6chten wir den durchschnittlichen Anstieg innerhalb der letzten Minute ermitteln. W\u00fcrden wir einen kleineren Wert angeben, so w\u00e4re der Wert pr\u00e4ziser, allerdings d\u00fcrfen wir nicht vergessen, dass Prometheus alle 5s die Metriken abfragt und die Anfrage selbst bei hoher Last etwas zeitverz\u00f6gert ankommen k\u00f6nnte. Als letztes multiplizieren wir den Wert mit 60, da &#8220;rate()&#8221; den durchschnittlichen Anstieg pro Sekunde angibt.<\/p>\n\n\n\n<p>Das folgende Bild zeigt das Ergebnis unserer Query:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-9.png\"><img loading=\"lazy\" decoding=\"async\" width=\"627\" height=\"568\" data-attachment-id=\"22252\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/image-9-4\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-9.png\" data-orig-size=\"627,568\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"image-9\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-9.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-9.png\" alt=\"\" class=\"wp-image-22252\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-9.png 627w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-9-300x272.png 300w\" sizes=\"auto, (max-width: 627px) 100vw, 627px\" \/><\/a><figcaption class=\"wp-element-caption\">Grafana Diagramm zeigt Anzahl der berechneten H\u00f6hen pro Minute mithilfe einer passenden PromQL an<\/figcaption><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"ergebnisse\">Jaeger<\/h4>\n\n\n\n<p>Metriken sind sch\u00f6n und gut, aber nicht alles. W\u00e4hrend jeder Entwickler sich wahrscheinlich schon mal mit dem Thema Logging besch\u00e4ftigt hat, hebt Tracing das Logging auf ein neues Level &#8211; und mit <a href=\"https:\/\/www.jaegertracing.io\/\" title=\"Jaeger\">Jaeger<\/a> k\u00f6nnen Traces erstellt, gesammelt und visualisiert werden.<\/p>\n\n\n\n<p><strong>Doch was ist Tracing?<\/strong><br>Tracing bedeutet Ablaufverfolgung. D.h. wir beobachten in unserem Fall, wann eine HTTP-Anfrage beginnt und wann sie aufh\u00f6rt. Wir beobachten zudem, was innerhalb unserer Anfrage geschieht und wie lange diese Abschnitte (=Spans) ben\u00f6tigen. Wir k\u00f6nnen somit die Funktionsaufrufe beobachten und Logs an diesen Aufrufe anh\u00e4ngen.<br>Der entscheidende Vorteil gegen\u00fcber Logs ist, dass Traces an Tracing-IDs gekn\u00fcpft sind. Zusammen mit Zeitstempel und Spans k\u00f6nnen wir leichter den Kontext nachvollziehen, w\u00e4hrend Logs ohne weiteres nur ein Tagebuch \u00fcber Ereignisse f\u00fchrt. Da Backends typischerweise mit mehreren Threads laufen, ist das Lesen von Logs erschwert, da Ereignisse &#8220;parallel&#8221; stattfinden.<\/p>\n\n\n\n<p>Der folgende Screenshot zeigt ein Trace in Jaeger UI an, welcher einen Aufruf mit mehreren Koordinaten an unser Backend zeigt:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/jaeger-traces-2.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"641\" data-attachment-id=\"22258\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/jaeger-traces-2\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/jaeger-traces-2.png\" data-orig-size=\"1038,650\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"jaeger-traces-2\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/jaeger-traces-2-1024x641.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/jaeger-traces-2-1024x641.png\" alt=\"\" class=\"wp-image-22258\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/jaeger-traces-2-1024x641.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/jaeger-traces-2-300x188.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/jaeger-traces-2-768x481.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/jaeger-traces-2.png 1038w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Ein Trace mit Spans in Jaeger UI<\/figcaption><\/figure>\n\n\n\n<p>Die Spans im Screenshot zeigen auf, wie lange die Berechnung der H\u00f6he zu einer Koordinate ben\u00f6tigt. Das Backend befand sich zu diesem Zeitpunkt unter Last und musste mehrere Anfragen parallel verarbeiten. In diesem Screenshot sieht man sch\u00f6n, dass die Berechnung mancher Koordinaten aufgrund von Threading und dem daraus resultierendem Scheduling deutlich mehr Zeit (z.B. 38.04ms) als andere Koordinaten ben\u00f6tigten (z.B. ~3ms &#8211; ~6ms). <\/p>\n\n\n\n<p>Wenn wir in Jaeger UI einen Span anklicken, so k\u00f6nnen wir die zugeh\u00f6rigen Logs sehen:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/jaeger-traces-3.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"664\" data-attachment-id=\"22259\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/jaeger-traces-3\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/jaeger-traces-3.png\" data-orig-size=\"1036,672\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"jaeger-traces-3\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/jaeger-traces-3-1024x664.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/jaeger-traces-3-1024x664.png\" alt=\"\" class=\"wp-image-22259\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/jaeger-traces-3-1024x664.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/jaeger-traces-3-300x195.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/jaeger-traces-3-768x498.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/jaeger-traces-3.png 1036w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Die Logeintr\u00e4ge eines Spans in Jaeger UI<\/figcaption><\/figure>\n\n\n\n<p>Unsere Elevation API muss einen Jaeger-Client implementieren, damit es Traces an den Jaeger-Agent \u00fcbermitteln kann. Es existieren <a href=\"https:\/\/www.jaegertracing.io\/docs\/1.31\/client-libraries\/\" title=\"verschiedene Sprachen Jaeger-Clients\">Jaeger-Clients f\u00fcr verschiedene Programmiersprachen<\/a>. So verwenden wir f\u00fcr die Elevation API die Bibliothek <a href=\"https:\/\/github.com\/jaegertracing\/jaeger-client-csharp\" title=\"jaeger-client-csharp\">jaeger-client-csharp<\/a>. Die komplette Architektur hinter Jaeger wird auf der Jaeger-<a href=\"https:\/\/www.jaegertracing.io\/docs\/1.31\/architecture\/\">Seite<\/a> kurz und knapp beschrieben.<\/p>\n\n\n\n<p><strong>HINWEIS:<\/strong> <strong>Die Entwicklung von jaeger-client-csharp wurde im Verlauf unseres Projektes eingestellt. Allgemein soll man anstatt auf Jaeger-Clients nun auf <a href=\"https:\/\/opentelemetry.io\/\" title=\"OpenTelemetry \">OpenTelemetry<\/a> setzen.<\/strong> <strong>Da das relativ sp\u00e4t aufkam, haben wir uns dazu entschieden, aus Zeit technischen Gr\u00fcnden nicht zu migrieren.<\/strong><\/p>\n\n\n\n<p>Die Einbindung der Bibliothek verlief relativ problemlos. Man kann jaeger-client-csharp so konfigurieren, dass die Verbindungsinformationen zum Jaeger-Agent \u00fcber Umgebungsvariablen festgelegt werden soll. Zus\u00e4ztlich haben wir daf\u00fcr gesorgt, dass Jaeger-Tracing \u00fcber eine Umgebungsvariable deaktiviert werden kann, um sp\u00e4ter einen Vergleich mit und ohne Jaeger durchf\u00fchren k\u00f6nnen. <\/p>\n\n\n\n<p>Anschlie\u00dfend l\u00e4sst man sich zur Anwendung \u00fcber den Konstruktur \u00fcberall eine Instanz von <em>ITracer<\/em> injizieren, um eigene Spans definieren zu k\u00f6nnen. Der Folgende Code Block zeigt, wie man einen eigenen Span definieren kann:<\/p>\n\n\n\n<!-- Tracing Implementation Demonstration in EudemDataset -->\n<!-- https:\/\/gist.github.com\/Morodar\/600382f1b224f4d93508cbaa4f339b8d -->\n<script src=\"https:\/\/gist.github.com\/Morodar\/600382f1b224f4d93508cbaa4f339b8d.js\"><\/script>\n\n\n\n<p>Mit <em>&#8220;tracer.BuildSpan(&#8230;).StartActive(finishSpanOnDispose:false)&#8221;<\/em> kann ein eigener Span zum Trace definiert und gestartet werden. Das zur\u00fcckgelieferte <em>&#8220;scope&#8221;<\/em>-Objekt muss manuell disposed werden, damit die Zeit f\u00fcr den Span gestoppt wird. Dank dem C# <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/csharp\/language-reference\/keywords\/using-statement\" title=\"using statement\">using statement<\/a> wird das <em>&#8220;scope&#8221;<\/em>-Objekt automatisch disposed, wenn die Methode <em>&#8220;GetAltitudeAt&#8221;<\/em> fertig ist &#8211; auch falls eine Exception irgendwo geworfen werden sollte.<\/p>\n\n\n\n<p>\u00dcber den Konstruktor lassen wir uns zus\u00e4tzlich ein <em>&#8220;ILogger&#8221;<\/em>-Objekt injizieren. Das ILogger Interface stammt von &#8220;Microsoft.Extensions.Logging&#8221; und stellt ein allgemeines Logging-Interface dar. Wenn wir dieses Interface verwenden, so werden Log-Eintr\u00e4ge automatisch mit dem zugeh\u00f6rigen Trace bzw. zum zugeh\u00f6rigen Span verkn\u00fcpft.<\/p>\n\n\n\n<p>Somit ist unsere Elevation API nicht nur in der Lage, Metriken zu liefern, sondern kann nun auch Jaeger mit Traces beliefern.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Die Elevation API benchmarken<\/h2>\n\n\n\n<p>Da wir nun ausgiebige M\u00f6glichkeiten geschaffen haben, unsere Elevation API zu \u00fcberwachen, wird es nun Zeit, unser Backend Lasttests zu unterziehen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Versuchsaufbau<\/h3>\n\n\n\n<p>Der Versuch wurde auf einen Homeserver durchgef\u00fchrt, auf dem <a href=\"https:\/\/www.proxmox.com\/de\/\" title=\"Proxmox\">Proxmox<\/a> installiert ist. <br>Der Server verf\u00fcgt \u00fcber folgende Hardware:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/www.intel.de\/content\/www\/de\/de\/products\/sku\/78867\/intel-celeron-processor-j1900-2m-cache-up-to-2-42-ghz\/specifications.html\">CPU: Intel Celeron J1900 CPU 4 Cores \/ 4 Threads @2.0GHz<\/a><\/li>\n\n\n\n<li>RAM: 2x 8GB DDR @ 1333 MHz<\/li>\n\n\n\n<li><a href=\"https:\/\/www.samsung.com\/de\/support\/model\/MZ-7TE250BW\/\" title=\"250 GB Sata Samung SSD 840\">250 GB Sata Samung SSD 840<\/a><br>(read: 540 MB\/s)<\/li>\n\n\n\n<li><a href=\"https:\/\/www.debian.org\/releases\/bullseye\/releasenotes\" title=\"Debian GNU\/Linux 11 (bullseye)\">Debian GNU\/Linux 11 (bullseye)<\/a><\/li>\n<\/ul>\n\n\n\n<p>Auf diesem Server wurde ein <a href=\"https:\/\/pve.proxmox.com\/wiki\/Linux_Container\" title=\"LXC Container\">LXC Container<\/a> erstellt, welchem folgende Ressourcen zugewiesen wurden:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>2 Kerne<\/li>\n\n\n\n<li>4 GB RAM<\/li>\n\n\n\n<li>512 MB SAWP<\/li>\n\n\n\n<li>20 GB Speicher<\/li>\n\n\n\n<li>keyctl=1, nesting=1<\/li>\n<\/ul>\n\n\n\n<p>Die Features keyctl und nesting wurden aktiviert, damit im LXC Container Docker Container laufen k\u00f6nnen.<\/p>\n\n\n\n<p>Das folgende Schaubild zeigt den Aufbau und Zusammenhang der Docker Container:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/aufbau.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"380\" data-attachment-id=\"22262\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/aufbau-3\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/aufbau.png\" data-orig-size=\"1638,608\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"aufbau\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/aufbau-1024x380.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/aufbau-1024x380.png\" alt=\"\" class=\"wp-image-22262\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/aufbau-1024x380.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/aufbau-300x111.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/aufbau-768x285.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/aufbau-1536x570.png 1536w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/aufbau.png 1638w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Zusammenhang der Docker Container<\/figcaption><\/figure>\n\n\n\n<p>Oben links sehen wir den Stresser, welcher auf einem anderem, leistungsst\u00e4rkeren Rechner laufen wird. Dieser Rechner ist \u00fcber ein Gbit-Netzwerk mit dem Homeserver verbunden. Im LXC Container des Homservers werden die Anfragen gegen die Elevation API laufen. Die daraus resultierenden Traces werden an den Jaeger-Agent \u00fcbermittelt, welcher Teil des <a href=\"https:\/\/www.jaegertracing.io\/docs\/1.31\/deployment\/\" title=\"Jaeger all-in-one Images\">Jaeger all-in-one Images<\/a> ist. Auf dem Server laufen node_exporter und cAdvisor, um Metriken zum System bereitzustellen. cAdvisor ben\u00f6tigt redis als Cache. Prometheus sammelt alle 5s Metriken von der Elevation API, node_exporter und cAdvisor ein. Grafana visualisiert die Metriken und bezieht die Daten aus Prometheus und Jaeger.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Versuchsdurchf\u00fchrung<\/h3>\n\n\n\n<p>Nachdem das Setup steht, wird es Zeit f\u00fcr die Versuche. Dazu sagen wir dem Stresser, 32 Anfragen \u00fcber 8 Workern an die Elevation API zu senden. Die Parameter der Anfragen sind zuf\u00e4llig generiert und liegen im Koordinatenbereich von Deutschland. Die Anzahl der Parameter variiert nach Szenario. Wir haben zun\u00e4chst 9 Szenarien definiert:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>GET mit 1 Koordinate<\/li>\n\n\n\n<li>GET mit 2 Koordinaten<\/li>\n\n\n\n<li>GET mit 5 Koordinaten<\/li>\n\n\n\n<li>POST mit 5 Koordinaten<\/li>\n\n\n\n<li>POST mit 10 Koordinaten<\/li>\n\n\n\n<li>POST mit 25 Koordinaten<\/li>\n\n\n\n<li>POST mit 50 Koordinaten<\/li>\n\n\n\n<li>POST mit 100 Koordinaten<\/li>\n\n\n\n<li>POST mit 10, 20, 30, &#8230;, 100 Koordinaten (die Anzahl der Koordinaten wird zuf\u00e4llig f\u00fcr alle 32 Anfragen gew\u00e4hlt)<\/li>\n<\/ul>\n\n\n\n<p>Die Szenarien laufen jeweils f\u00fcr mindestens 5 Minuten. Anschlie\u00dfend werden die Ergebnisse ausgewertet, die Anzahl der Parameter angepasst und der Vorgang wiederholt.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Wer viel misst, misst Mist<\/h3>\n\n\n\n<p>Nach einiger Zeit brach die Performance rapide ab. Es ging soweit, dass die Elevation API nicht mehr reagierte. Auch die anderen Komponenten reagierten nicht mehr. Ein Blick in Proxmox zeigte den Grund:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/elevation-api-overload.png\"><img loading=\"lazy\" decoding=\"async\" width=\"501\" height=\"327\" data-attachment-id=\"22269\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/elevation-api-overload\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/elevation-api-overload.png\" data-orig-size=\"501,327\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"elevation-api-overload\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/elevation-api-overload.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/elevation-api-overload.png\" alt=\"\" class=\"wp-image-22269\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/elevation-api-overload.png 501w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/elevation-api-overload-300x196.png 300w\" sizes=\"auto, (max-width: 501px) 100vw, 501px\" \/><\/a><figcaption class=\"wp-element-caption\">Auslastung des LXC Container in Proxmox<\/figcaption><\/figure>\n\n\n\n<p>Uns war bewusst, dass das Jaeger all-in-one Image die Traces im RAM speicherte. Uns war allerdings nicht bewusst, wie schnell der RAM unter Dauerlast des Backends voll werden kann. Nachdem auch der SWAP voll wurde, ging nichts mehr. Entsprechend wurden die Container nach jedem Szenariodurchlauf neu gestartet, sodass der RAM beim testen nicht voll\u00e4uft.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Messergebnisse<\/h3>\n\n\n\n<p>Nachdem alle Szenarien durchgef\u00fchrt wurden, kommen wir nun zur Auswertung. Fangen wir mit den Jaeger Traces an. Es folgen Screenshots aus Jaeger UI, die jeweils die Dauer von 1000 Anfragen zeigen.<br><em>(Die Bilder k\u00f6nnen angeklickt werden, um sie in ihrer vollen Gr\u00f6\u00dfe anzuzeigen)<\/em><\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST10-JAEGER.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"136\" data-attachment-id=\"22271\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/post10-jaeger\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST10-JAEGER.png\" data-orig-size=\"1723,229\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"POST10-JAEGER\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST10-JAEGER-1024x136.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST10-JAEGER-1024x136.png\" alt=\"\" class=\"wp-image-22271\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST10-JAEGER-1024x136.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST10-JAEGER-300x40.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST10-JAEGER-768x102.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST10-JAEGER-1536x204.png 1536w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST10-JAEGER.png 1723w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">POST 10 Szenario Jaeger<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST25-JAEGER.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"143\" data-attachment-id=\"22272\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/post25-jaeger\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST25-JAEGER.png\" data-orig-size=\"1721,241\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"POST25-JAEGER\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST25-JAEGER-1024x143.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST25-JAEGER-1024x143.png\" alt=\"\" class=\"wp-image-22272\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST25-JAEGER-1024x143.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST25-JAEGER-300x42.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST25-JAEGER-768x108.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST25-JAEGER-1536x215.png 1536w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST25-JAEGER.png 1721w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">POST 25 Szenario Jaeger<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST50-JAEGER.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"134\" data-attachment-id=\"22273\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/post50-jaeger\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST50-JAEGER.png\" data-orig-size=\"1706,224\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"POST50-JAEGER\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST50-JAEGER-1024x134.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST50-JAEGER-1024x134.png\" alt=\"\" class=\"wp-image-22273\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST50-JAEGER-1024x134.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST50-JAEGER-300x39.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST50-JAEGER-768x101.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST50-JAEGER-1536x202.png 1536w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST50-JAEGER.png 1706w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">POST 50 Szenario Jaeger<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST100-JAEGER.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"141\" data-attachment-id=\"22274\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/post100-jaeger\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST100-JAEGER.png\" data-orig-size=\"1691,233\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"POST100-JAEGER\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST100-JAEGER-1024x141.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST100-JAEGER-1024x141.png\" alt=\"\" class=\"wp-image-22274\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST100-JAEGER-1024x141.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST100-JAEGER-300x41.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST100-JAEGER-768x106.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST100-JAEGER-1536x212.png 1536w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST100-JAEGER.png 1691w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">POST 100 Szenario Jaeger<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST-RANDOM-JAEGER.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"128\" data-attachment-id=\"22275\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/post-random-jaeger\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST-RANDOM-JAEGER.png\" data-orig-size=\"1697,212\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"POST-RANDOM-JAEGER\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST-RANDOM-JAEGER-1024x128.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST-RANDOM-JAEGER-1024x128.png\" alt=\"\" class=\"wp-image-22275\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST-RANDOM-JAEGER-1024x128.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST-RANDOM-JAEGER-300x37.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST-RANDOM-JAEGER-768x96.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST-RANDOM-JAEGER-1536x192.png 1536w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/POST-RANDOM-JAEGER.png 1697w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">POST 10-100 (random) Szenario Jaeger<\/figcaption><\/figure>\n\n\n\n<p>F\u00fcr GET und POST zeigt sich bis 25 Koordinaten eine relativ gleichm\u00e4\u00dfige Verteilung der Anfragendauer. Gr\u00f6\u00dfere Abst\u00e4nde zeigten sich ab 50 Koordinaten. Das liegt daran, dass der Stresser immer 32 Anfragen in einer Schleife (mit 8 Workern) durchgef\u00fchrt hat und anschlie\u00dfend gewartet hat, bis alle Antworten ankamen, bevor die n\u00e4chsten 32 Anfragen abgeschickt wurden, um den Server nicht mit zu vielen parallelen Anfragen zu \u00fcberfordern &#8211; schlie\u00dflich wurde der Container mit 2 Containern betrieben und die Arbeit war sehr CPU lastig. <br>Wir wollten im Benchmark die Worker Zahl zum besseren Vergleichen gleich halten, da bei den POST 100 Anfragen mit mehreren Workern der Server so stark \u00fcberlastet war, dass das Monitoring nicht mehr funktionierte. Prometheus konnte die Metriken von der Elevation API nur noch sehr langsam bis gar nicht abfragen, da die Antwort bis zu einer Minute dauern konnte, wodurch wir keine zuverl\u00e4ssige Messwerte erhielten w\u00fcrden.<br>Die Reduzierung der Worker hatte zufolge, dass in den Szenarien mit 1 bis 25 Koordinaten die CPU-Last nie die 100% erreicht hat. Erst ab 50+ Parametern wurden die 100% knapp erreicht. Entsprechend ist folgende Tabelle, die die Anzahl der berechneten H\u00f6henpunkte pro Minute pro Szenario anzeigt, mit Vorsicht zu genie\u00dfen. Die Zahlen der Szenarien mit 1-25 Parametern stellen nicht die absolute Obergrenze dar:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-15.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"175\" data-attachment-id=\"22301\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/image-15-4\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-15.png\" data-orig-size=\"1658,284\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"image-15\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-15-1024x175.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-15-1024x175.png\" alt=\"\" class=\"wp-image-22301\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-15-1024x175.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-15-300x51.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-15-768x132.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-15-1536x263.png 1536w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-15.png 1658w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Auswertung der Szenarien<\/figcaption><\/figure>\n\n\n\n<p>Die Tabelle zeigt, dass umso mehr Koordinaten auf einmal mitgeschickt werden, umso mehr H\u00f6henberechnungen pro Minute m\u00f6glich sind. Das hat drei Gr\u00fcnde:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Die CPU wurde in den Szenarien 1-25 nicht vollst\u00e4ndig ausgenutzt<\/li>\n\n\n\n<li>HTTP-Overhead, Parsen, Logging<\/li>\n\n\n\n<li>Der Konstruktor von EudemDataset<\/li>\n<\/ol>\n\n\n\n<p>Die Gr\u00fcnde zum ersten Punkt wurden bereits genannt. Zum zweiten Punkt mussten wir feststellen, dass in unserem Fall das intensivere Logging mehr Zeit gekostet hat, als das Tracing an sich &#8211; das wird der Vergleich im \u00fcbern\u00e4chsten Abschnitt &#8220;Der Preis von Tracing&#8221; deutlich zeigen. <\/p>\n\n\n\n<p>Zum dritten Punkt mussten wir \u00fcber Jaeger feststellen, dass das \u00f6ffnen der TIF Dateien \u00fcber Gdal.OpenEx unterschiedlich lang ben\u00f6tigt. Die folgenden beiden Screenshots zeigen die Traces aus Jaeger:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-12.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"664\" data-attachment-id=\"22281\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/image-12-4\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-12.png\" data-orig-size=\"1635,1060\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"image-12\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-12-1024x664.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-12-1024x664.png\" alt=\"\" class=\"wp-image-22281\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-12-1024x664.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-12-300x194.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-12-768x498.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-12-1536x996.png 1536w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-12.png 1635w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Gdal.OpenEx ben\u00f6tigte 0.5 ms<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-11.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"664\" data-attachment-id=\"22280\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/image-11-4\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-11.png\" data-orig-size=\"1632,1059\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"image-11\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-11-1024x664.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-11-1024x664.png\" alt=\"\" class=\"wp-image-22280\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-11-1024x664.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-11-300x195.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-11-768x498.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-11-1536x997.png 1536w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-11.png 1632w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Gdal.OpenEx ben\u00f6tigte 60ms<\/figcaption><\/figure>\n\n\n\n<p>Die Zeiten von Gdal.OpenEx lagen zwischen 0.5ms und 60ms &#8211; diese Zeiten wurden au\u00dferhalb des Benchmarkings gemessen, sprich der Server war im Idle. Die genaue Ursache dahinter konnten wir nicht herausfinden. Wir wissen lediglich, dass die verwendete Bibliothek ein Wrapper um die C und C++ Bibliothek GDAL ist, welches wir nicht ohne weiteren Aufwand mit unserem Monitoring\/Tracing und Debugging durchleuchten k\u00f6nnen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Wie lange dauert die Berechnung eines H\u00f6henpunktes?<\/h3>\n\n\n\n<p>Hierf\u00fcr k\u00f6nnen wir eine Anfrage mit mehreren Parametern gegen die Elevation API schicken und das Ergebnis in Jaeger pr\u00fcfen:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/jaeger-ohne-last.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"515\" data-attachment-id=\"22291\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/jaeger-ohne-last\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/jaeger-ohne-last.png\" data-orig-size=\"1050,528\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"jaeger-ohne-last\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/jaeger-ohne-last-1024x515.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/jaeger-ohne-last-1024x515.png\" alt=\"\" class=\"wp-image-22291\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/jaeger-ohne-last-1024x515.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/jaeger-ohne-last-300x151.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/jaeger-ohne-last-768x386.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/jaeger-ohne-last.png 1050w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>Die Berechnung eines H\u00f6henpunktes dauert zwischen 3.5ms bis 6ms, wobei die 6ms eher eine Ausnahme bilden. W\u00fcrde man das bei 2 Kernen auf eine Minute hochrechnen, so w\u00fcrde man auf folgende Werte kommen:<br>1000ms\/3,5ms*2*60(1\/min) = 34.285 1\/min<br>1000ms\/6ms*2*60(1\/min) = 20.000 1\/min<br>Diese Zahlen zeigen uns, dass unsere Benchmarks im erwartetem Bereich liegen und dass noch etwas Potential nach oben vorhanden ist.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Der Preis von Tracing<\/h3>\n\n\n\n<p>Wir haben soeben festgestellt, dass etwa 34.285 Berechnungen pro Minute das maximale theoretische Limit f\u00fcr unser Setup ist. Da wir bislang auf etwa 27.000 Berechnungen pro Minute gekommen sind, stellt sich nun die gro\u00dfe Frage, welche Performanceeinbu\u00dfen das Tracing darstellt oder ob die Ursache woanders liegt.<\/p>\n\n\n\n<p>Dazu haben wir das POST 50 Szenario erneut durchgef\u00fchrt. Dabei haben wir aber folgende F\u00e4lle Unterschieden:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>POST 50 mit Jaeger und eigenem Trace Logging<\/li>\n\n\n\n<li>POST 50 ohne Jaeger, aber mit eigenem Trace Logging<\/li>\n\n\n\n<li>POST 50 mit Jaeger, ohne eigenem Trace Logging<\/li>\n\n\n\n<li>POST 50 ohne Jaeger und ohne eigenem Trace Logging<\/li>\n<\/ul>\n\n\n\n<p>Das folgende Schaubild zeigt die unterschiedlichen Messergebnisse. Die Diagramme zeigen die Dauer und Anzahl der Anfragen. Unter jedem Diagramm steht die Anzahl der berechneten H\u00f6hendaten pro Minute und welche Features deaktivert wurden:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-13.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"411\" data-attachment-id=\"22292\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/07\/entwicklung-und-benchmarking-einer-eigenen-elevation-api\/image-13-4\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-13.png\" data-orig-size=\"2529,1015\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"image-13\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-13-1024x411.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-13-1024x411.png\" alt=\"\" class=\"wp-image-22292\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-13-1024x411.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-13-300x120.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-13-768x308.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-13-1536x616.png 1536w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/image-13-2048x822.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">POST 50 Vergleich zwischen verschiedenen Szenarien<\/figcaption><\/figure>\n\n\n\n<p>Wir mussten mit erstaunen feststellen, dass das Tracing mit Jaeger nur einen kleinen Performance Unterschied ausmacht und vielmehr das Logging f\u00fcr die Performanceeinbu\u00dfen zust\u00e4ndig ist. F\u00fcr den Fall ohne Jaeger und nahezu ohne Logging kommen wir auf etwa 33.000 Berechnungen pro Minute, was ziemlich nah an unser theoretisches Limit von 34.285 Berechnungen pro Minute kommt (schlie\u00dflich ber\u00fccksichtigt unser theoretisches Limit nicht das Verarbeiten einer HTTP-Anfrage).<\/p>\n\n\n\n<p>Wenn wir also unsere Anwendung weiter optimieren wollen und nicht auf das Logging verzichten wollen (was wir auf jeden Fall nicht wollen), dann m\u00fcssten wir uns das Thema Logging (und vor allem dessen Konfiguration) genauer unter die Lupe nehmen. Aus Zeit Gr\u00fcnden sind wir allerdings nicht mehr dazu gekommen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Learnings aus dem Monitoring<\/h3>\n\n\n\n<p>Die Reise \u00fcber das Thema Monitoring kommt zu einem Ende. Wir haben viel \u00fcber die genanntenn Tools gelernt und ziehen folgendes R\u00e9sum\u00e9:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Die gezeigten Tools funktionieren &#8220;out of the box&#8221;\n<ul class=\"wp-block-list\">\n<li>Die Tools (vor allem cAdvisor) erheben mehr Metriken als man \u00fcblicherweise braucht. Aufgrund dieser Tatsache sollte man sich die Metriken genau anschauen und in der Dokumentation recherchieren, wie sich unbenutzte Metriken deaktivieren lassen, um unn\u00f6tige Performance-Einbu\u00dfe zu vermeiden.<\/li>\n\n\n\n<li>F\u00fcr uns war es nicht immer klar, was einzelne Metriken bedeuten oder wie diese zu interpretieren sind. Zu cAdvisor haben wir geschrieben, dass die CPU Last bei 10% liegt. Da der Container zwei Kerne zugewiesen bekommen hat, liegt hier die Grenze nicht bei 100%, sondern bei 200% &#8211; etwas, was man in diesem Fall ber\u00fccksichtigen muss.<\/li>\n\n\n\n<li>Wenn man sich die Doku zu Prometheus, cAdvisor, Jaeger und Co. anschaut, so wird man feststellen, dass es sehr viele Einstellungen gibt, durch die man sich k\u00e4mpfen muss, wenn es einmal auf etwas ankommt. Das ist mit entsprechendem Aufwand verbunden.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Es ist nicht einfach, eigene und sinnvolle Metriken zu finden. Der Counter zur Anzahl der berechneten H\u00f6hepunkte ist simpel und effektiv.\n<ul class=\"wp-block-list\">\n<li>Doch wenn es darum geht, weitere sinnvolle Metriken zu finden, so kommt man schnell ins Gr\u00fcbeln. Neben Anfragendauer oder Anzahl an Parametern wird es schwierig, weitere sinnvolle Metriken zu finden &#8211; oder gibt es keine weiteren? <\/li>\n\n\n\n<li>Wie sinnvoll eine Metrik ist, kann sich meist erst beim Ausprobieren herausstellen. <br>Z.B. haben wir einen Gauge implementiert, welcher uns anzeigt, wie viele Anfragen gerade parallel verarbeitet werden. Was in der Theorie praktisch klingt, kann sich in der Praxis als nutzlos erweisen. Wenn n\u00e4mlich Anfragen unter 10s bearbeitet werden, kann es durchaus sein, dass diese gar nicht in der Statistik auftauchen, da die Abtastrate von Prometheus zu gering ist und eine h\u00f6here Abtastrate wiederum mit einer erh\u00f6hten Anzahl an Daten und Performance-Verlust einhergeht.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Tracing in Kombination mit angeh\u00e4ngten Logs sind sehr m\u00e4chtig.\n<ul class=\"wp-block-list\">\n<li>Wir konnten dadurch Bottlenecks identifizieren<\/li>\n\n\n\n<li>Allerdings kostet Tracing und Logging Performance. Diesen Verlust muss man ggf. pr\u00fcfen und man sollte sicherstellen, dass dadurch auch ein Mehrwert entsteht.<\/li>\n\n\n\n<li>Man muss sich damit auseinandersetzen, welche Daten man behalten m\u00f6chte. Die Erfahrung im Benchmarking hat gezeigt, dass schnell viele Daten anfallen k\u00f6nnen. Ein sinnvoller Filter ist also Pflicht.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"fazit\">Fazit<\/h2>\n\n\n\n<p>Die Grundidee des Projekts war es zu sehen, wie sich ein Server unter Last verh\u00e4lt. Es ging darum, nachzuvollziehen, an welchen Stellen Bottlenecks entstehen, sie nachvollziehen zu k\u00f6nnen und wie diese vermieden werden k\u00f6nnen. \u00dcber die Dauer der Entwicklung hat sich herausgestellt, dass es nicht so einfach ist und einiges an Analyse, Planung und Refactoring ben\u00f6tigt war, um zu dem Ergebnis zu kommen, welches wir erzielen wollten. Im Endeffekt, haben wir eine realistische Anwendung geschaffen, welche uns Daten bereitstellt. Wir haben f\u00fcr dieses System Metriken analysiert und zum Monitoring aufbereitet, um anschlie\u00dfend die Auswirkungen hoher Last auf das System testen und auf die Ergebnisse reagieren zu k\u00f6nnen. Eine solche Erfahrung ist f\u00fcr gew\u00f6hnlich nur in Produktivsystemen m\u00f6glich und deswegen f\u00fcr uns zu gro\u00dfen Teilen unbekannt. Um einen realit\u00e4tsnahen Einstieg in das Thema Monitoring, Tracing und Optimierung von Produktivumgebungen zu erlangen ist dies wohl der beste Weg, den man gehen kann. Wir haben eine Menge Dinge gelernt, \u00fcber die wir ansonsten erst stolpern w\u00fcrden, wenn es zu sp\u00e4t ist.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Worum geht&#8217;s? Im Rahmen der Veranstaltung &#8220;System Engineering and Management&#8221; sollte ein Softwareprojekt unserer Wahl und mit besonderem Augenmerk auf Systemarchitektur durchgef\u00fchrt, analysiert und dokumentiert werden. F\u00fcr unser Projekt haben wir uns entschieden, einen besonderen Schwerpunkt auf Monitoring zu legen. Das Projekt bestand also aus drei gr\u00f6\u00dferen Teilprojekten: dem Backend selbst, ein Stresser, der Last [&hellip;]<\/p>\n","protected":false},"author":1039,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1,649,262,2],"tags":[],"ppma_author":[863],"class_list":["post-22034","post","type-post","status-publish","format-standard","hentry","category-allgemein","category-interactive-media","category-rich-media-systems","category-system-engineering"],"aioseo_notices":[],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":28550,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2026\/02\/21\/entwicklung-einer-verteilten-cloud-anwendung-am-beispiel-eines-multiplayer-spiels\/","url_meta":{"origin":22034,"position":0},"title":"Entwicklung einer verteilten Cloud-Anwendung am Beispiel eines Multiplayer Spiels","author":"Tom Bestvater","date":"21. February 2026","format":false,"excerpt":"Einleitung Den meisten sollte das Spielprinzip von \u201cCookie Clicker\u201d bekannt sein: Ein Klick auf einen Keks erh\u00f6ht den Spielstand um einen Punkt. Das Spiel ist endlos, hat keine Punktegrenze. Es geht darum, im Leaderboard nach oben zu klettern. Im Rahmen der Vorlesung \u201cSystem Engineering and Management\u201d (143101a)\u00a0 erweiterten wir das\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2026\/02\/Auth-Flow-Diagram-1.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2026\/02\/Auth-Flow-Diagram-1.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2026\/02\/Auth-Flow-Diagram-1.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2026\/02\/Auth-Flow-Diagram-1.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":23579,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/08\/30\/google-geodata-visualizer\/","url_meta":{"origin":22034,"position":1},"title":"Google Geodata Visualizer","author":"sk331","date":"30. August 2022","format":false,"excerpt":"Ein Projekt von Kai Kustermann, Michael Litschko, Sarah Mauff und Sebastian K\u00f6pp Einleitung Im Sommersemester 2022 haben wir uns als 4-k\u00f6pfige Gruppe dazu entschlossen, einen Google Geodata Visualizer zu erstellen. Das Projekt ist aus der Idee einer McDonald\u2019s-Achievement-Card entstanden. Die Idee war eine Website, die dem Benutzer anzeigt, welche McDonald\u2019s\u2026","rel":"","context":"In &quot;Cloud Technologies&quot;","block_context":{"text":"Cloud Technologies","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/scalable-systems\/cloud-technologies\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/08\/16.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":28021,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2025\/09\/13\/multiplayer-web-game-mit-aws-schiffe-versenken\/","url_meta":{"origin":22034,"position":2},"title":"Multiplayer Web-Game mit AWS | Schiffe versenken","author":"Leon Obertopp","date":"13. September 2025","format":false,"excerpt":"Projektidee: Im Rahmen der Vorlesung \"Software Development for Cloud Computing\" sollen die Studierenden in Gruppen ein eigenes Projekt, mit Hilfe von in der Vorlesung gezeigten Cloud Technologien umsetzen. Wir hatten Anfangs Probleme ein geeignetes Thema zu finden, da unser Wissenstand im Thema Cloud nicht besonders gro\u00df war. Letztendlich haben wir\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2025\/09\/image-4.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2025\/09\/image-4.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2025\/09\/image-4.png?resize=525%2C300&ssl=1 1.5x"},"classes":[]},{"id":22395,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/27\/applikationsinfrastruktur-einer-modernen-web-anwendung\/","url_meta":{"origin":22034,"position":3},"title":"Applikationsinfrastruktur einer modernen Web-Anwendung","author":"Jannik Smidt","date":"27. February 2022","format":false,"excerpt":"ein Artikel von Nicolas Wyderka, Niklas Schildhauer, Lucas Cr\u00e4mer und Jannik Smidt Projektbeschreibung In diesem Blogeintrag wird die Entwicklung der Applikation- und Infrastruktur des Studienprojekts sharetopia beschrieben. Als Teil der Vorlesung System Engineering and Management wurde besonders darauf geachtet, die Anwendung nach heutigen Best Practices zu entwickeln und dabei kosteneffizient\u2026","rel":"","context":"In &quot;Interactive Media&quot;","block_context":{"text":"Interactive Media","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/interactive-media\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Bildschirmfoto_2022-02-27_um_18.59.07.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Bildschirmfoto_2022-02-27_um_18.59.07.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Bildschirmfoto_2022-02-27_um_18.59.07.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Bildschirmfoto_2022-02-27_um_18.59.07.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Bildschirmfoto_2022-02-27_um_18.59.07.png?resize=1050%2C600&ssl=1 3x"},"classes":[]},{"id":28011,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2025\/09\/11\/cloud-native-security-scanner\/","url_meta":{"origin":22034,"position":4},"title":"Cloud-native Security Scanner","author":"Tim Ruff","date":"11. September 2025","format":false,"excerpt":"Dieses Projekt wurde im Rahmen der Vorlesung \u201eSoftware Development for Cloud Computing\u201c umgesetzt. Ausgangslage und Projektidee Unser bisheriger Fokus im Studium lag haupts\u00e4chlich auf Themen der IT-Security und Machine Learning, weshalb wir beide bis auf die grundlegenden Vorlesungen zum Thema Software Entwicklung kaum Erfahrungen in diesem Bereich gesammelt haben. Aus\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2025\/09\/image.jpeg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2025\/09\/image.jpeg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2025\/09\/image.jpeg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2025\/09\/image.jpeg?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2025\/09\/image.jpeg?resize=1050%2C600&ssl=1 3x"},"classes":[]},{"id":28084,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2025\/09\/14\/springboot-zu-serverless-probleme-und-paradigmen\/","url_meta":{"origin":22034,"position":5},"title":"Springboot zu Serverless: Probleme und Paradigmen","author":"Julian Schniepp","date":"14. September 2025","format":false,"excerpt":"Im Rahmen der Vorlesung \u201eSoftware Development for Cloud\u00a0Computing\u201c sollte jedes Team ein eigenes Cloud\u2011Projekt umsetzen. Unser Projekt Taskflow sollte dabei ein serverloses Todo\u2011Management\u2011System werden. Ziel war es dabei, neue und vor allem industrierelevante Cloud\u2011Technologien praktisch zu erlernen. Der Backend\u2011Teil ist mit Spring\u2011Boot realisiert, welcher \u00fcber AWS\u00a0Lambda und API\u00a0Gateway bereitgestellt modular\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"jetpack_sharing_enabled":true,"authors":[{"term_id":863,"user_id":1039,"is_guest":0,"slug":"jp112","display_name":"Eric Prytulla","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/5ae05b98444ae84da70df6ff8240ae44d6fad71768c3b57de48ea0b8e793d522?s=96&d=mm&r=g","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""}],"_links":{"self":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/22034","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/users\/1039"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/comments?post=22034"}],"version-history":[{"count":85,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/22034\/revisions"}],"predecessor-version":[{"id":25341,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/22034\/revisions\/25341"}],"wp:attachment":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/media?parent=22034"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/categories?post=22034"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/tags?post=22034"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/ppma_author?post=22034"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}