Download-Geschwindigkeit messen und in Google DataStudio darstellen

Alles beginnt mit einer fixen Idee. Meine war es, die Download-Geschwindigkeit meines Internet-Anschlusses zu messen. Doch das ist nur aussagekräftig, wenn man es regelmäßig macht. Und um das ganze abzurunden, sollte man die Ergebnisse doch irgendwie noch in einem bunten Diagramm darstellen können. Aus der Idee wurde also ein Plan und schließlich ein Projekt.

Die Aufgabe lautet also: Ein Script soll in regelmäßigen Abständen Test-Dateien herunterladen. Die Dauer dafür und der Zeitpunkt des Tests schreibe ich in eine CSV-Datei, die mit GoogleDrive synchronisiert ist. Von dort werden die Ergebnisse im DataStudio von Google automatisch ausgelesen.

Schritt 1 - das automatisierte Download-Script

Damit der Test möglichst unterschiedliche Szenarien abdeckt, wollte ich nicht nur eine sondern mehrere Dateien verschiedener Größe anbieten. Dazu habe ich auf http://speedtest.ftp.otenet.gr zurückgegriffen. Dort werden verschiedene Dateigrößen zum Download angeboten.

Außerdem will ich jede Datei mehr als ein mal herunterladen und schließlich nach jedem Download eine Pause einlegen.

Die Hauptfunktionen möchte ich kurz erläutern:

  • In den ersten beiden Schleife wird jeder Eintrag aus sourceFileSizes einmal durchlaufen und zwar so oft, wie mit loops festgelegt:

for sourceFileSize in “${sourceFileSizes[@]}”; do while [ $i -lt $loops ]; do … done done

  • Innerhalb der Schleife wird im Grund nur die Test-Datei per wget an einen definierten Ort heruntergeladen. Außerdem soll natürlich die Dauer dafür gemessen werden. Das funktioniert ganz einfach und extrem präzise in Nanosekunden bzw. über den Unix-Timestamp.

startTime=$(($(date +%s%N)))

wget –quiet –output-document=$destinationFolder$sourceFileSize"Mb.db.tmp" $sourceBaseUrl$sourceFileSize"Mb.db"

endTime=$(($(date +%s%N)))

delayNsecs=$(($endTime - $startTime))

  • Die Berechnung der Geschwindigkeit ist etwas kompliziert, da ich auf der Shell nicht ohne weiteres Dezimalzahlen (float numbers) verarbeiten kann. Ich muss also mit awk arbeiten. Awk hingegen greift nicht auf die lokalen Variablen zu. Diese muss ich mit dem Parameter -v erst explizit übergeben. Am Ende entstehen dann Zeile wie diese, die im Grunde nur MByte in MBit umrechnen (mit 8 multiplizieren) und dann durch die Dauer in Sekunden dividieren. Das Ergebnis ist dann die Geschwindigkeit MBit/Sekunde - und damit perfekt vergleichbar mit der versprochenen Geschwindigkeit des Anbieters.

MBitPerSec= $(awk -v sourceFileSize=$sourceFileSize -v delayMSecs=$delaySecs ‘BEGIN{printf “%.4f\n”, ( sourceFileSize * 8 / delayMSecs)}’)

  • Schließlich wird der ganze Spaß natürlich noch in die CSV-Datei geschrieben:

echo “$(date ‘+%Y-%m-%d %H:%M:%S’), $delayNsecs, $delaySecs, $MBitPerSec, $sourceFileSize"MByte”" » $journalFile

  • Am Ende wird entweder das Script verlassen, wenn zuletzt die 1GByte-Datei heruntergeladen wurde. Da das Script mehrmals am Tag läuft, will ich das Volumen nicht unnötig strapazieren. Oder es wird eine definierte Pause eingelegt, damit sich die Leitung abkühlen kann.

Das komplette Script gibt es auf github.

Das ganze muss nun nur in der CronTabelle des Systems regelmäßig aufgerufen werden. Der Zielordner

/share/Download/Speedtest/

wird außerdem mit GoogleDrive synchronisiert.

Die fertige Datei besitzt fünf Spalten, die den Zeitpunkt, die Dauer und die heruntergeladene Datei beinhalten:

column1,column2,column3,column4,column5 2017-10-31 11:48:36,1072562724,1.072562724,7.45877124105611,1MByte 2017-10-31 11:48:37,899356112,0.899356112,8.89525282950432,1MByte 2017-10-31 11:48:38,1002897956,1.002897956,7.97688334305469,1MByte

Anmerkung: Ich nutze den Netzwerkspeicher von QNAP, das TS-431. Für diesen wird ein Backup & Sync-Plugin angeboten, das lokale Ordner mit einem Ordner in GoogleDrive synchronisiert.

Schritt 2 - der Community Connector für das DataStudio

Das Google DataStudio bringt von Hause aus schon eine Reihe von Schnittstellen mit, über die es möglich ist, auf z.B. Datenbanken oder Online-Dienste zurückzugreifen um in Echzeit an allerlei Messreihen zu kommen. Doch leider fehlt hier bisher ein Verbindung zu CSV-Dateien. Den Connector musste ich mir also erst selber erstellen.

Der Connector ist im Moment noch sehr einfach gehalten. Es ermöglicht keine großen Anpassungen und ist sicherlich noch verbesserungsfähig (Link zum Connector):

function getConfig() { var config = { configParams: [ { type: “INFO”, name: “csvConnector”, text: “The CSV-Connector currently supports a fixed amount of three columns. Name them column1, column2 and column3. Column1 is the dimension, column2 holds the metrics and column3 may be used as an additional category. You may change the label in the next window.” } ,{ type: “TEXTINPUT”, name: “url”, helpText: “If you want to use a CSV-file from GoogleDrive, use this format where 123 at the end is your document id: https://drive.google.com/uc?export=download&id=123", displayName: “Provide the url to your csv file.” }

\]

}; return config;

};

var csvDataSchema = [ { name: ‘column1’, label: ‘column1’, dataType: ‘STRING’, semantics: { conceptType: ‘DIMENSION’ } }, { name: ‘column2’, label: ‘column2’, dataType: ‘NUMBER’, semantics: { “isReaggregatable”: true, conceptType: ‘METRIC’ } },{ name: ‘column3’, label: ‘column3’, dataType: ‘STRING’, semantics: { “isReaggregatable”: false, conceptType: ‘DIMENSION’ }

} ];

function getSchema(request) {

return {schema: csvDataSchema};

};

function isAdminUser() { return true; }

function csvToObject(array) {

var headers = array[0];

var jsonData = []; for ( var i = 1, length = array.length; i < length; i++ ) { var row = array[i]; var data = {}; for ( var x = 0; x < row.length; x++ ) { data[headers[x]] = row[x]; } jsonData.push(data);

}

return jsonData;

} /* function stringToObject(string, separator) { var object = {};

var array = string.split(separator);

for (var i = 0; i < array.length; i++) {

if (i % 2 === 0) { 

  object\[array\[i\]\] = array\[i + 1\];

} else { 

  continue;
  
}

}

return object } */

function getData(request) {

/* I DONT GET SPLIT TO WORK SO FOR NOW THIS ONLY SUPPORTS PREPARED AND WORKING SHARING URL FOR GOOGLE DRIVE if (request.configParams.isGoogleDrive == “true”) { var urlString = request.configParams.url.toString();

var urlArray = urlString.split("?");

  var params = stringToObject(urlArray, '"');

  var docId = params\["id"\];

  var url = "https://drive.google.com/uc?export=download&id=" + docId;

} else {

  var url = request.configParams.url;

} */

var url = request.configParams.url;

var dataSchema = [];

request.fields.forEach(function(field) { for (var i = 0; i < csvDataSchema.length; i++) { if (csvDataSchema[i].name === field.name) { dataSchema.push(csvDataSchema[i]); break; } } });

var csvFile = UrlFetchApp.fetch(url);

var csvData = Utilities.parseCsv(csvFile);

var sourceData = csvToObject(csvData);

var data = [];

sourceData.forEach(function(row) { var values = []; dataSchema.forEach(function(field) { switch(field.name) { case ‘column1’: values.push(row.column1); break; case ‘column2’: values.push(row.column2); break; case ‘column3’: values.push(row.column3); break; default: values.push(’’); } }); data.push({ values: values }); });

return { schema: dataSchema, rows: data };

};

function getAuthType() { var response = { “type”: “NONE” }; return response; }

 

Leider unterstützt der Connector bisher nur drei Spalten mit vorgegebene Spalten-Namen. In einer nächsten Version sollte der Connector die Datei bereits im Vorfeld auslesen um die Spalten-Konfiguration selber zu erkennen.

 

Schritt 3 - Darstellung im DataStudio

Im Data Studio ein Diagramm erstellen, dass die Messwerte der regelmäßigen Test-Download darstellt: Darum muss ich mich noch kümmern.

 

Link zu GitHub mit dem Quellcode

Link zum Connector

Einführung und Doku auf developers.google.com

kleinere Beispiele auf benlcollins.com

andere Projekte auf github.com