Bericht aus dem Maschinenraum (Teil 1): map.ffnw.de

Auf unseren Treffen haben wir schon häufiger darüber diskutiert, dass es für Außenstehende manchmal nicht so leicht ersichtlich ist, was wir als Freifunk Nordwest – außer dem Bewerben und Aufstellen von Routern überhaupt so machen. Um das zu ändern, wollen wir zukünftig in regelmäßigen Abständen Einblicke in unsere Arbeit an der Technik geben und vorstellen, welche Neuerungen wir planen und umsetzen und welche Problemlösungen wir entwickeln.

Den Start dieser Reihe mache heute ich und stelle euch ein kleineres Projekt vor, an welchem ich in den letzten Wochen einige Zeit gearbeitet habe. Zu tun hat dies mit unserer Freifunk-Karte unter map.ffnw.de.

Dem Einen oder Anderen wird aufgefallen sein, dass unserer Freifunk-Karte in letzter Zeit doch etwas langsam gewesen ist. Weil auch mir irgendwann sauer aufstieß, dass mein Laptop etwa 8 und mein Smartphone sogar fast 20 Sekunden benötigte um die Seite zu laden, hatte ich mir vorgenommen, mich während unseres Freifunk-Hackathons der Sache anzunehmen und die Performance nach Möglichkeit zu verbessern.

Gesagt getan und ich fing an, die Performance-Probleme einzugrenzen. Dabei fielen mir im wesentlichen zwei Dinge auf:

  • Das Javascript, welches die Map darstellt, verbrauchte ziemlich viel Rechenleistung – Mein CPU-Lüfter heulte bei jedem Aufruf hörbar auf
  • Die Datenmenge, die das Javascript lud, war durch unsere in den letzten Jahren stark gewachsene Routerzahl wirklich immens geworden und lag bei etwa 2 Megabyte (!). Viel zu viel für eine doch recht einfache Anwendung; für mobilen Aufruf sowieso.

Einige Stunden lang versuchte ich nun zunächst das Javascript zu optimieren, indem ich nach und nach einzelne Funktionen, die ich des Leistungshungers verdächtigte, abschaltete. Ohne Erfolg. Mit einem kurzen Prototypen versuchte ich dann zu testen, ob vielleicht einfach nur die verwendete Bibliothek für die Kartendarstellung, leaflet, an seine Leistungsgrenzen geraten war. Aber die Darstellung mehrere hundert Punkte und Linien auf einer Karte packte leaflet dann doch völlig problemlos. Mit weiteren Experimenten wuchs die Erkenntnis, dass vielleicht der Zeitpunkt gekommen sein könnte, mit der Entwicklung einer leistungsfähigeren Anzeige zu beginnen.

Dieser Neuentwicklung sollte beim „Fundament“ beginnen, also bei der effizienten Übermittlung der Kartendaten an das Javascript. Der aktuelle Mechanismus funktioniert etwa so:

In jeder unserer Hoods (Subnetze) läuft auf einem Supernode ein sog. Hopglass-Server, der die Daten der Freifunk-Router dieser Hoods einsammelt und diese auf Port 4000 per http zur Verfügung stellt (siehe zB http://os02.sn.ffnw.de:4000/nodes.json ) . Der Server, auf dem unsere Freifunk-Karte liegt, holt die Daten von dort regelmäßig ab, speichert sie mit den Daten anderer Hoods zwischen und sorgt für deren Aktualisierung. Das „Einsammeln“ der Daten wird dabei von einem kleinen Nodejs-Script erledigt. Ein funktionierender Ansatz, aber mit viel Optimierungspotenzial:

  • Das Script, welches die Daten von den Hood-Servern einsammelt, bildet eigentlich nichts Anderes als einen Cache. Warum also an dieser Stelle nicht auf ein bewährtes, fertiges,performantes Caching-System zurückgreifen?
  • Die Server liefern die Daten als unkomprimiertes json (große Datenmengen!)
  • Das Datenformat ist eine Eigenentwicklung, warum nicht etwas standardisiertes wie zB netjson oder geojson verwenden?
  • Das Script erzeugt manchmal verhältnismäßig viele Prozesse und viel Last

Daraus entwickelte sich der folgende Lösungsansatz:

  • Auf dem Map-Server wurde die Konfiguration des verwendeten nginx-Webservers angepasst, sodass dieser Anfragen nach Daten aus den jeweiligen Hoods zunächst zentral entgegen nimmt und diese dann als Proxy an den zuständigen Hood-Server weiterleitet:

(Definiton eines Cache in der nginx.conf:)

proxy_cache_path /tmp/nginx/ keys_zone=one:10m levels=1:2;

(Aktivierung der Weiterleitung (proxy) und gzip-Komprimierung für die *.json-Dateien:)

location ~ /ll-dataproxy/hoods/([^/]+)/ { 
                proxy_cache_lock on; 
                proxy_cache_valid any      1m; 
                proxy_cache_lock_age 5s; 
                proxy_cache one; 
                resolver 8.8.8.8; 
 
                gzip on; 
                #gzip_proxied any; 
                gzip_types application/json; 
 
                rewrite    /ll-dataproxy/hoods/([^/]+)/(.*) /$2 break; 
                proxy_pass http://$1.sn.ffnw.de:4000; 
}
  • Damit dies nicht bei jeder Anfrage erneut passieren muss, ist an dieser Stelle auch ein Cache eingerichtet, der nach einer Minute erneuert wird. Die Daten für jede Hood sind damit gemäß dem Schema https://srv11.ffnw.de/ll-dataproxy/hoods/os02/nodes.json erreichbar.
  • Ebenfalls in der nginx-Konfiguration wurde gzip aktiviert (siehe oben), wodurch die Daten zur Übertragung stark komprimiert werden können. Insbesondere bei den json-Dateien, die häufig Listen Hunderter Elemente mit jeweils gleichnamigen Subelementen besitzen, kann die gzip-Komprimierung glänzen und staucht die Dateien teilweise auf bis zu 1/10 ihrer ursprünglichen Größe zusammen!
  • Auf einem Freifunktreffen empfahl Oliver die Verwendung von geojson, da dies von leaflet direkt verarbeitet werden kann. Unter https://srv11.ffnw.de/ll-dataproxy/hoods/os02/geojson/nodelist.json und https://srv11.ffnw.de/ll-dataproxy/hoods/os02/geojson/graph.json können die geojson-Dateien für Knoten und Meshverbindungen abgerufen werden.

Mit dieser Konfiguration haben wir die Datenverarbeitung auf der Serverseite erheblich robuster und effizienter gestaltet. Die Daten können nun auch wesentlich schneller und weniger traffic-intensiv abgerufen werden. Der nächste Schritt ist nun, das entsprechende Gegenstück für den Browser zu schreiben, was durch diese Grundlage einfacher als zuvor möglich sein wird. Dann steht einer neuen Map, die bei gängiger Hardware und Internetverbindung gut funktioniert nichts mehr entgegen.

Wir halten euch auf dem Laufenden.

Schreibe einen Kommentar