Wie man Speedtest auf einem Linux-Server verwendet, um Internetgeschwindigkeiten grafisch zu überprüfen, zu speichern und zu melden.

Nach einer Reihe von Problemen mit schlechter Breitbandkonnektivität, die ich hatte, entschied ich mich, die Mbit/s-Geschwindigkeit, die ich von meinem Provider regelmäßig erhielt, zu überwachen. Besonders schlechte Zahlen sah ich beim Versuch, abends Dateien herunterzuladen, mit viel höheren Geschwindigkeiten, die sehr früh am Morgen erreicht wurden.

Ich habe einen Linux-Debian-Server in einer Ecke, der meine Test- und Entwicklungsmaschine für von ISPConfig gehostete Websites ist, sowie einige Let’s Encrypt-Codes, mit denen ich gerne herumspiele, also habe ich nach einer Software gesucht, die Bandbreiten-Tests ermöglicht, die von einer Linux-Befehlszeile aus ausgeführt werden kann, die die Grundlage für ein automatisiertes Shell-Skriptsystem bilden könnte, um die benötigten Rohdaten zu erzeugen. Ich wollte die Daten in einer SQL-Datenbank speichern, um die Abfrage einfach zu machen (ich könnte dann leicht mehr Daten sammeln und nur eine kleinere Teilmenge für Zeiträume extrahieren, die mich interessieren) und ein Web-Frontend haben, das ein einfaches Diagramm erstellen könnte, um die Daten zu visualisieren und die Verbindungsprobleme hervorzuheben.

Das erste Ergebnis meiner Suche war der wirklich nützliche Artikel von Antonio Valencia unter: https://www.howtoforge.com/tutorial/check-internet-speed-with-speedtest-cli-on-ubuntu/

Nachdem ich die Anweisungen zur Installation von Speedtest befolgt hatte, zeigte sich in kürzester Zeit, dass ich damit Tests auf einer Vielzahl von Internet-Servern durchführen und auch Ausgaben im CSV-Format erzeugen konnte, was sich gut eignet, um mit minimalem Aufwand in der Softwareentwicklung direkt in eine SQL-Tabelle importiert zu werden.

Für die relativ geringe Datenmenge, die erzeugt werden sollte, entschied ich mich für SQLite als Backend-Speicher und eine schnelle Suche nach den verfügbaren Open-Source-Javascript-basierten Grafikbibliotheken führte mich zu chart.js, das ein einfaches, sauberes Design mit einer einfachen Datenschnittstelle, aber viel Möglichkeiten zum Optimieren erweiterter Optionen bei Bedarf hat. Die Konvertierung von SQL-Daten, um nur die Teilmenge der Daten zu extrahieren, die ich grafisch darstellen wollte, mit der Ausgabe über JSON über einen einfachen PHP-Code war der richtige Weg.

Insgesamt musste ich also ein System entwerfen, das so aussah:

Ein Linux-Server, der Speedtest als Cronjob ausführt – vielleicht 1 pro Stunde – wobei die Speedtest-Ausgabe in einer SQLite-Datenbank gespeichert wird – alles gesteuert durch eine Bash-Shell-Skriptdatei. Ein Web-Frontend, das eine Mischung aus HTML, CSS, Javascript und PHP ist, um Daten aus SQLite zu extrahieren und ein Balkendiagramm zu erstellen, das die erreichten Mbit/s-Werte für die letzten 24 Stunden (oder jeden anderen Zeitraum, den ich wählen könnte) anzeigt.

Ein kleines Experimentieren mit dem Ausführen von Speedtest ein Dutzend oder so mal interaktiv zeigte mir, dass es ein paar Server gab, die Ergebnisse zu liefern schienen, die eng mit der Art von Geschwindigkeiten übereinstimmten, die ich erlebte. Ich hielt es für eine gute Idee, gegen mehr als einen Server zu testen, um besser zu verstehen, wie sich meine Mbit/s-Zahlen auf den Standort des Zielservers und die Tageszeit in einer anderen Zeitzone auswirken.

Wenn Sie mitverfolgen und ein ähnliches System für sich selbst einrichten möchten, dann müssen Sie einen oder mehrere Server aus Hunderten von Servern auswählen, die für den Speedtest zur Verfügung stehen, um diese für Ihren Standort zu verwenden.

1 Voraussetzungen

  • ein Linux-Server – ich benutze Debian 9.1 – stretch
  • tty Zugriff auf den Server mit Root-Login – Ich benutze PuTTY von einem Windows-Laptop aus.
  • ISPConfig installiert und eine Website mit einem FTP-Konto konfiguriert – ich benutze 3.1.6 mit Apache-Set als Webserver (Sie können mit nur einem Webserver arbeiten, mit ein paar kleinen Änderungen an den folgenden Anweisungen).
  • PHP – Ich benutze 7.0, aber das sollte auch mit den meisten früheren Versionen funktionieren.
  • FTP-Client – ich benutze Filezilla – und PureFTPd läuft auf dem Server.
  • nano – oder Ihr bevorzugter visueller Editor

Ich gehe hier davon aus, dass Sie sich mit der Anmeldung an einem Linux-Server auskennen, wie Sie die Verzeichnisse umgehen können, das Layout, in dem Ihr Webserver Dateien erwartet und wie man FTP-Dateien in diese Verzeichnisse schreibt.

Hier sind die detaillierten Schritte, die es Ihnen ermöglichen, dies alles einzurichten.

2 Geschwindigkeitsprüfung installieren

Melden Sie sich bei Ihrem Linux-Server als root an und führen Sie den Befehl aus:

# pip install speedtest-cli

Weitere Informationen finden Sie unter https://www.howtoforge.com/tutorial/check-internet-speed-with-speedtest-cli-on-ubuntu/ und https://pypi.python.org/pypi/speedtest-cli, wenn Sie Probleme haben.

Hinweis: Speedtest und Speedtest-cli sind bei meiner Installation identisch, daher werde ich im Folgenden nur auf Speedtest verweisen.

3 SQLite3 installieren

# apt-get install sqlite3

Benutze das Äquivalent für deine Distribution, wenn apt-get nicht für dich ist.

4 Bandbreite erstellen.sh

Gib den folgenden Bash-Skriptcode in eine Datei ein und speichere ihn unter /usr/local/etc/bandwidth.sh – wir werden ihn später bearbeiten, um ihn für dich spezifisch zu machen.

#!/bin/bash
# run speedtests against 3 servers and save all output results in CSV file for import to sqlite db
#
# run by cronjob once per hour
#
#
function getCSVString () {
	# if speedtest failed (e.g. it couldn't access a server) we need to create a dummy zero entry for this time slot
	
	# get a timestamp string in the same format that speedtest generates - and we need UTC time
	local RIGHTNOW=$(date --utc +%Y-%m-%dT%H:%M:%SZ)
	
	# which server are we testing against?
	if [ $1 = "5443" ] 
	then
		echo "5443,Fasthosts Internet,Gloucester,$RIGHTNOW,73.09,0.0,0.0,0.0"
	fi
	if [ $1 = "1234" ] 
	then
		echo "1234,Uno,Milton Keynes,$RIGHTNOW,168.27,0.0,0.0,0.0"
	fi
	if [ $1 = "1783" ] 
	then
		echo "1783,Comcast,\"San Francisco, CA\",$RIGHTNOW,8420.0,0.0,0.0,0.0"
	fi
	
# test/debug case only
    if [ $1 = "199999999" ]
    then
        echo "99999,Test,Test,$RIGHTNOW,99.99,0.0,0.0,0.0"
    fi
}

function runTest () {
	# run speedtest against named server with csv output saved in tmp file
	/usr/local/bin/speedtest --csv --server $1 > /usr/local/etc/speedtest.tmp
	if [ $? -gt 0 ] 
	then
		# speedtest failed so create a zero entry in place of any error message
		getCSVString $1 > /usr/local/etc/speedtest.tmp
	fi

	# save output ready for next server test
	cat /usr/local/etc/speedtest.tmp >> /usr/local/etc/speedtest.csv
}

# main
#######
# run speedtest against 3 servers and save all output results in csv file
cd /usr/local/etc

# clear csv file - needs to be empty at start of run
rm -f /usr/local/etc/speedtest.csv

############################################
# test/debug case - forces speedtest to fail
############################################
# runTest "199999999"
# sleep 5
####### comment out after testing ##########
############################################

runTest "5443"
sleep 10

runTest "1234"
sleep 10

runTest "1783"
sleep 1

# now import the csv data in to sqlite db
sqlite3 -batch /usr/local/etc/bandwidth.db <<"EOF"
.separator ","
.import /usr/local/etc/speedtest.csv bandwidth
EOF

# add current run csv to backup
cat /usr/local/etc/speedtest.csv >> /usr/local/etc/speedtest.bak

Ich entschuldige mich für meinen „Gürtel und Klammern“-Ansatz, vollständige Pfade für Dateien überall zu verwenden, auch wenn sie nicht benötigt werden. Es ist genau so, wie ich es gerne mache. Zögern Sie nicht, dies zu verbessern, wenn Sie mit der Bearbeitung von Bash-Skripten vertraut sind.

Legen Sie die Dateieigenschaften fest, um dieses Skript ausführbar zu machen:

# chmod 0700 bandwidth.sh

5 Erstellen einer SQLite-Datenbank

Erstellen Sie die bandwidth.db SQLite-Datenbank in /usr/local/etc :

#sqlite3 bandwidth.db

und dann, an der Eingabeaufforderung sqlite>, eine neue Tabelle mit dem folgenden Befehl erstellen (verpassen Sie nicht das letzte Semikolon):

sqlite> CREATE TABLE IF NOT EXISTS "bandwidth" ("serverid" INTEGER NOT NULL , "sponsor" VARCHAR NOT NULL , "servername" VARCHAR NOT NULL , "times" DATETIME PRIMARY KEY NOT NULL UNIQUE , "distance" FLOAT NOT NULL , "ping" FLOAT NOT NULL , "download" FLOAT NOT NULL , "upload" FLOAT NOT NULL );
sqlite> .quit

Dadurch wird eine Tabelle namens Bandbreite mit Feldern erstellt, die direkt auf die CSV-Formatausgabe von Speedtest abgebildet werden.

6 Eine Liste der Server abrufen

Sie benötigen eine Liste der Server, die von Speedtest verwendet werden.

# speedtest --list > servers.txt

Überprüfen Sie nun in der Datei servers.txt die numerischen IDs der Server, gegen die Sie Ihre Tests durchführen möchten.

# nano servers.txt

Die Datei wird ähnlich aussehen wie diese:

Retrieving speedtest.net configuration...
 5833) Hub Network Services Ltd (Newport, Wales) [57.50 km]
 5938) Spectrum Internet (Cardiff, Great Britain) [65.89 km]
 5443) Fasthosts Internet (Gloucester, Great Britain) [74.31 km]
 6504) Secure Web Services Ltd (Shrewsbury, Great Britain) [78.64 km]
 7265) Unitron Systems & Development Ltd (Telford, Great Britain) [87.11 km]
 8225) Exascale Limited (Wolverhampton, Great Britain) [96.08 km]
 3110) zero.net.uk Ltd (Studley, Great Britain) [96.12 km]
12401) Dragon WiFi LTD (Haverfordwest, United Kingdom) [120.78 km]
 1153) Warwicknet Ltd. (Coventry, Great Britain) [125.18 km]
 1685) Vodafone UK (Newbury, Great Britain) [153.25 km]
 4384) Iomart (Leicester, Great Britain) [157.40 km]
 1234) Uno (Milton Keynes, Great Britain) [170.71 km]
 3504) TNP Ltd. (Manchester, Great Britain) [170.93 km]
11747) Vispa (Manchester, United Kingdom) [170.93 km]

Die Server-IDs befinden sich auf der linken Seite. Die Zahl am Ende jeder Zeile ist die Schätzung, die der Speedtest aus der Entfernung, in Kilometern, zwischen Ihrem Standort und dem des Servers gemacht hat, obwohl ich mir nicht sicher bin, ob er zu genau ist und sich von Lauf zu Lauf ändern kann; die Testserver werden in der Reihenfolge dieser Entfernung aufgelistet, die mit der nächstgelegenen beginnt. Das Testen gegen die Server, die sich an der Spitze dieser Liste befinden, sollte Ihnen theoretisch die schnellsten Pings und die besten Download- und Upload-Geschwindigkeiten liefern, verglichen mit Servern, die viel weiter unten in der Liste stehen.

7 Server-IDs auswählen und bandwidth.sh bearbeiten

Jetzt wäre es an der Zeit, Speedtest manuell gegen eine Auswahl der verschiedenen verfügbaren Server-IDs auszuführen und zu sehen, welche Art von Ergebnissen Sie erhalten. Ich habe ein paar Server in meiner Nähe in Großbritannien und einen in Kalifornien zum Vergleich ausgewählt. Das Format des zu verwendenden Befehls ist:

# speedtest --server 1234

Die Ausgabe, die Sie sehen, wird ähnlich sein wie:

Retrieving speedtest.net configuration...
Testing from xxxxxxx (n.n.n.n)...
Retrieving speedtest.net server list...
Selecting best server based on ping...
Hosted by Uno (Milton Keynes) [187.87 km]: 33.243 ms
Testing download speed................................................................................
Download: 1.60 Mbit/s
Testing upload speed...............................................................................................
Upload: 0.55 Mbit/s

Nachdem Sie die Server ausgewählt haben, die Sie verwenden möchten, legen Sie die numerischen Server-IDs (ich habe 3 Server verwendet, aber Sie können dies bei Bedarf ändern) in die entsprechenden Zeilen in bandwidth.sh ein.

runTest "5443"
sleep 10

runTest "1234"
sleep 10

runTest "1783"
sleep 1

Sie müssen auch den Code in der Fehlerroutine anpassen, die einen Dummy-Eintrag erstellt, wenn der Speedtest bei einem bestimmten Lauf fehlschlägt.

	# which server are we testing against?
	if [ $1 = "5443" ] 
	then
		echo "5443,Fasthosts Internet,Gloucester,$RIGHTNOW,73.09,0.0,0.0,0.0"
	fi
	if [ $1 = "1234" ] 
	then
		echo "1234,Uno,Milton Keynes,$RIGHTNOW,168.27,0.0,0.0,0.0"
	fi
	if [ $1 = "1783" ] 
	then
		echo "1783,Comcast,\"San Francisco, CA\",$RIGHTNOW,8420.0,0.0,0.0,0.0"
	fi

Die Zahlen nach $RIGHTNOW (z.B. 73.09) sind die Entfernungen in Kilometern von Ihrem Standort zum jeweiligen Server. Ich verwende diese nirgendwo, also sind sie nur ein Platzhalter und können beliebige Zahlenwerte sein.

Beachten Sie bei diesem Beispiel von 1783, dass wir Anführungszeichen auf die Position setzen und sie entkommen lassen müssen, um sie in die CSV-Datei zu bekommen, die wir erstellen. Die Anführungszeichen werden hier benötigt, da diese Stelle zufällig ein Komma enthält. Ohne die escapten Anführungszeichen würde das Komma als CSV-Feldbegrenzer behandelt, was zu einem Problem mit dem SQLite-Import führen würde. Wenn der von Ihnen ausgewählte Server einen ähnlichen Ortstext mit einem Komma enthält, müssen Sie die escapten Anführungszeichen verwenden.

8 Einen Cronjob einrichten

Richten Sie einen Cronjob ein, der einmal pro Stunde (oder so oft Sie wollen) ausgeführt wird, um /usr/local/etc/bandwidth.sh auszuführen. Wenn Sie ISPConfig ausführen, können Sie damit einen Cronjob planen.

Alternativ kannst du auch über die Linux-Befehlszeile eingeben:

# crontab -e

Du solltest etwas Ähnliches sehen (denk daran, dass du als ‚root‘ angemeldet bist):

* * * * * /usr/local/ispconfig/server/server.sh 2>&1 | while read line; do echo `/bin/date` "$line" >> /var/log/ispconfig/cron.log; done
* * * * * /usr/local/ispconfig/server/cron.sh 2>&1 | while read line; do echo `/bin/date` "$line" >> /var/log/ispconfig/cron.log; done
1 * * * * /usr/local/etc/bandwidth.sh 2>&1

Wenn Sie ISPConfig nicht ausführen, kann dies zunächst leer sein. Füge die letzte Zeile genau wie oben gezeigt hinzu – der Abstand ist wichtig -, um das Shell-Skript ab 00:01 Uhr auszuführen und dann jede Stunde, jeden Tag zu wiederholen. Sie können natürlich verschiedene Zeiten wählen. (Wenn Sie dies zum ersten Mal ausführen, fragt crontab Sie, welchen Editor Sie verwenden möchten – ich wähle Nano.)

9 PHP open_basedir einstellen

Füge /usr/local/etc zum PHP open_basedir-Eintrag für die Website hinzu. In ISPConfig finden Sie dies auf der Registerkarte Optionen für die Website.

Dies ermöglicht es dem bandwidth.php-Code, auf die SQLite-Datenbank, die wir gerade erstellt haben, in diesem Verzeichnis zuzugreifen.

Wir hätten dies überspringen können, wenn wir uns entschieden hätten, die Datenbank in einem Verzeichnis zu erstellen, das bereits als barrierefrei eingestellt ist, wie z.B. /var/www/clients/client1/web1/web1/web/, aber das wäre aus Sicherheitssicht eine schlechte Wahl.

10 Bandbreite erstellen.php

Sie müssen diesen Code in eine Datei namens bandwidth.php auf Ihrem Server im Basis-Webdokumentverzeichnis kopieren. Wenn Sie ISPConfig verwenden, ist dies so etwas wie /var/www/clients/client1/web1/web1/web/.

<html lang="en"> 
<head> 
<meta charset="utf-8" /> 
<title>Bandwidth Monitor - download speeds in last 24 hours</title>
<script src="scripts/Chart.bundle.min.js"></script>
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
</head>
<body>
<h3 style="font-family: 'Roboto', sans-serif; text-align: center">
Download speeds - last 24 hours
</h3>
<canvas id="myChart" width="1100px" height="500px"></canvas>
<script>
	var bandwidth_data = <?php
	// generate json formatted data from sqlite db
	// Specify sqlite database name and path
	// apache server has setting for PHP open_basedir to include /usr/local/etc
	class MyDB extends SQLite3 {
		function __construct() {
			$this->open('/usr/local/etc/bandwidth.db');
		}
	}
	$db = new MyDB();
	if(!$db) {
		echo $db->lastErrorMsg();
	} else {
		echo "";
	}
	// select the most recent 24 entries to display the last 24 hours of test results (3 servers are tested per hour)
	$sql =<<<EOF
	SELECT serverid, strftime("%H:%M", times) || " " || strftime("%d/%m/%Y", times) AS timestamp, sponsor, servername, download
	FROM bandwidth WHERE serverid = 1234 ORDER BY times LIMIT 24 OFFSET (SELECT COUNT(*)/3 FROM bandwidth)-24;
	EOF;
	$ret = $db->query($sql);
	if(!$ret){
		echo $db->lastErrorMsg();
	} else {
		while($row = $ret->fetchArray(SQLITE3_ASSOC) ) {
			$results[] = $row;
		}
	$ukdata = json_encode($results);
	}
	echo $ukdata;
	$db->close();
	?>
	;
	// extract the fields we want to chart from the JSON data
	var bwlabels = [], bwdata = [];
	var mbps, bvalue;
	for (var i = 0; i < bandwidth_data.length ; i++){
		bwlabels.push(bandwidth_data[i].timestamp);
		// convert bps to Mbps rounded with 3 decimal places in local format
		mbps = Math.round(bandwidth_data[i].download/1000).toFixed(3)/1000;
		bvalue = mbps.toLocaleString(undefined, { minimumFractionDigits: 3 });
		bwdata.push(bvalue);
	}
	var bar_color = 'rgba(0, 128, 255, 0.9)';
	var ctx = document.getElementById("myChart").getContext('2d');
	var myChart = new Chart(ctx, {
		type: 'bar',
		data: {
			labels: bwlabels,
			datasets: [{
				label: 'Mbps download',
				data: bwdata,
				backgroundColor: bar_color,
				borderColor: bar_color,
				borderWidth: 1
			}]
		},
		options: {
			// we override the default tooltip which drops trailing zeros even though we already put them there
			tooltips: {
				callbacks: {
					label: function(tooltipItem, data) {
						var value = data.datasets[0].data[tooltipItem.index];
						var label = 'download: ';
						var retvalue = value.toLocaleString(undefined, { minimumFractionDigits: 3 });
						return label + ' ' + retvalue + ' Mbps';
					}
				}
			},
			responsive: false,
			scales: {
				xAxes: [{
					ticks: {
						autoSkip: false,
						maxTicksLimit: 24
					}
				}],
				yAxes: [{
					ticks: {
						beginAtZero:true
					}
				}]
			}
		}
	});
</script>
</body>
</html>

Bearbeiten Sie diese Datei, um die Server-ID zu verwenden, über die Sie berichten möchten. Ich benutze den Server 1234 in meinem Beispiel hier, da ich festgestellt habe, dass dieser Server nach dem Erkunden einiger Tage von Daten Mbit/s-Figuren produzierte, die am ehesten mit den Geschwindigkeiten übereinstimmen, die ich zu bekommen glaubte. Die serverid befindet sich in der WHERE-Klausel der SQL SELECT-Anweisung:

SELECT serverid, strftime("%H:%M", times) || " " || strftime("%d/%m/%Y", times) AS timestamp, sponsor, servername, 
download 
FROM bandwidth 
WHERE serverid = 1234 
ORDER BY times 
LIMIT 24 OFFSET (SELECT COUNT(*)/3 FROM bandwidth)-24;

Was genau macht diese SQL-Anweisung? Wenn Sie nicht mit SQL vertraut sind, dann lassen Sie uns jeden Teil betrachten.

a. SELECT ist der Befehl zum Lesen von Datensätzen aus einer SQL-Datenbanktabelle, gefolgt von den zu lesenden Feldern und anderen Optionen.

b. strftime(„%H:%M“, Zeiten) ||| “ “ “ „ || strftime(„%d/%m/%Y“, Zeiten) AS-Zeitstempel

ist es, die datetime Zeichenkette, die speedtest in seiner CSV-Ausgabe erstellt hat, auf etwas etwas benutzerfreundlicheres zu formatieren. Ich möchte britische formatierte Daten, also nimmt dies eine Zeichenkette wie „2017-08-31T12:02:51.898186Z“ und verwandelt sie in „12:02 31/08/2017“. Es ist einfacher, diese Neuformatierung direkt in der SQL-Anweisung durchzuführen, als sie anschließend verarbeiten zu müssen. Die Zeiten hier werden UTC/GMT sein, was für mich in Ordnung ist, aber du kannst dies ändern; z.B. wenn du US-formatierte Daten willst, dann ändere diesen zweiten Teil in strftime(„%m/%d/%Y“, times).

c. serverid, Zeitstempel, Sponsor, Servername, Download sind die Felder, die wir aus der SQL-Tabelle lesen und in unserem JSON-Objekt anlegen wollen.

d. FROM Bandbreite ist der Name der SQL-Tabelle, aus der wir lesen.

e. Wobei serverid = 1234 die Teilmenge der zu lesenden Tabelle setzt – ändern Sie dies entsprechend der Serverid, die Sie verwendet haben, und Sie können Daten für mehr als einen Server lesen wollen – aber das wird das Diagramm komplizieren.

f. ORDER BY times legt die Sortierreihenfolge der Ausgabe fest – wir möchten, dass sie nach dem Zeitstempel sortiert wird, den der Speedtest für jeden Lauf festgelegt hat.

g. LIMIT 24 beschränkt die Ausgabe auf 24 Datensätze, da wir nur Daten im Wert von 24 Stunden anzeigen wollen und weil unser Cronjob einmal pro Stunde ausgeführt wird. Wenn Sie zweimal pro Stunde laufen würden, dann müssten Sie dies auf 48 einstellen, um 24 Stunden Daten zu erhalten.

h. OFFSET (SELECT COUNT(*)/3 FROM bandwidth)-24; wir wollen die letzten 24 Datensätze aus der Tabelle, da es sich um die neuesten Einträge handelt, die uns interessieren, also müssen wir einen OFFSET angeben, um mit dem LIMIT übereinzustimmen. Ohne dies würden wir immer die ersten 24 Datensätze in der Tabelle bekommen und nicht die 24 neuesten. Um den richtigen Offset zu erhalten, zählen wir alle Datensätze in der Tabelle mit (SELECT COUNT(*)), dividieren diesen durch 3 (da wir den Speedtest dreimal pro Stunde ausführen, einmal für jeden der 3 verschiedenen Server) und ziehen dann 24 von dieser Summe ab, um die richtige OFFSET-Position zu erhalten, so dass LIMIT 24 dann die gewünschten Datensätze erhält.

Wenn du das Bash-Skript so geändert hast, dass es etwas anderes als 3 verschiedene Servertests pro Stunde ausführt, dann passe diesen /3-Teil entsprechend an. Wenn Sie nur gegen einen Server testen, dann wird die Division überhaupt nicht benötigt.

Sie können auch die Gesamtgröße des Diagramms anpassen, wo ich den einfachen Weg der festen Codierung einer für meinen Bildschirm geeigneten Größe gewählt habe – sie wird in dieser Zeile eingestellt:

<canvas id="myChart" width="1100px" height="500px"></canvas>

11 Holen Sie sich eine lokale Kopie der Dateien.

Ich bevorzuge lokale Versionen von allen CSS- und JS-Bibliotheksdateien (aber keine Google-Schriften), die in einer Webseite benötigt werden, und wenn Sie derselbe sind, dann müssen Sie eine Kopie auf Ihrem Server von Chart.bundle.min.js erhalten und sie in das Verzeichnis /var/wwww/clients/client1/web1/web1/web/scripts auf Ihrem Server platzieren (oder das Basisverzeichnis, das für Sie geeignet ist).

Sie können die Datei herunterladen unter: https://cdnjs.cloudflare.com/ajax/libs/Chartajs/2.6.0/Chart.bundle.min.js

Wenn Sie keine lokale Kopie verwenden möchten, bearbeiten Sie bandwidth.php, um stattdessen auf die öffentliche CDN-Version zu verweisen. Ändere einfach diese Zeile:

<script src="scripts/Chart.bundle.min.js"></script>

zu diesem Thema:

<script src="https://cdnjs.cloudflare.com/ajax/libs/Chartajs/2.6.0/Chart.bundle.min.js"></script>

12 PHP in ISPConfig aktivieren

Vergiss nicht, PHP in deinen Website-Einstellungen zu aktivieren, wenn dies nicht bereits eingestellt ist.

13 Bandbreite.php in den Browser laden

Wir sind endlich fertig. Sobald das bandwidth.sh-Shell-Skript einige Male ausgeführt werden konnte, um einige Daten zu generieren (oder Sie könnten es zuerst mehrmals manuell ausführen), zeigen Sie Ihren Browser auf Ihrer Linux-Server-Website, laden Sie bandwidth.php und Sie sollten so etwas sehen:

Und ja, mein Breitband ist wirklich so schlecht!

Abschließend noch ein paar zusätzliche Punkte, die es wert sind, behandelt zu werden:

14 Balkendiagrammausgabe

Wir möchten darauf hinweisen, dass die in der SQL-Tabelle gespeicherten Download- und Upload-Zahlen in bps und nicht in Mbps vorliegen (zusammen mit einer verwirrenden Anzahl von Dezimalstellen – Zahlen wie 1533681.5922415722). Dies ist nur die Art und Weise, wie Speedtest die Daten im CSV-Modus erzeugt. Um die Mbps-Zahl statt bps auf der y-Achse des Balkendiagramms anzuzeigen, gibt es ein paar Zeilen, die im Javascript-Code in bandwidth.php enthalten sind, um die Konvertierung durchzuführen:

	mbps = Math.round(bandwidth_data[i].download/1000).toFixed(3)/1000; 
	bvalue = mbps.toLocaleString(undefined, { minimumFractionDigits: 3 });

Die Verwendung von toLocaleString sollte die richtige dezimale Zeichensetzung (a „.“ oder „,“) einfügen, wie sie durch die Gebietsschema-Einstellung Ihres Browsers festgelegt wurde, aber dies ist implementierungsabhängig und etwas inkonsistent. Wenn Sie sehen, . statt, und es ärgert Sie dann Globalize ist der Weg, um dies zu beheben. Siehe unten „18 Weitere Schritte und Ideen“.

Ein paar weitere Zeilen sind nötig, um die standardmäßige Hover-Code-Behandlung von abschließenden Nullen zu überschreiben, da chart.js normalerweise „2.000“ nur als „2“ anzeigt, was nicht das ist, was ich will, besonders nachdem ich mir die Mühe gemacht habe, sicherzustellen, dass sie überhaupt vorhanden sind:

    // we over-ride the default tooltip which drops trailing zeros even though we already put them there
    tooltips: {
        callbacks: {
            label: function(tooltipItem, data) {
                var value = data.datasets[0].data[tooltipItem.index];
                var label = 'download: ';
                var retvalue = value.toLocaleString(undefined, { minimumFractionDigits: 3 });
                return label + ' ' + retvalue + ' Mbps';
            }
        }
    },

Dies ist ein schönes Beispiel dafür, wie Sie in chart.js’aufschlüsseln‘ und die Art und Weise, wie es Dinge macht, ändern können.

Außerdem habe ich die Diagrammoptionen eingestellt, um den Zeitstempel auf der x-Achse für jeden Balken zu drucken:

	xAxes: [{
		ticks: {
			autoSkip: false,
			maxTicksLimit: 24
		}

Die Standardoption (mit autoSkip auf true gesetzt) führte zu einigen seltsam aussehenden Lücken in den Labels. Sie müssen maxTicksLimit ändern, wenn Sie etwas anderes als 24 Einträge anzeigen möchten.

Wenn Sie mehr Hilfe beim Ändern einer der chart.js-Optionen benötigen oder nicht bekommen können, was Sie arbeiten möchten, dann besuchen Sie bitte die spezifischen chart.js Stack Overflow-Seiten – es gibt dort viele nützliche Informationen – https://stackoverflow.com/questions/tagged/chart.js – Verwenden Sie einfach das Suchfeld, um einzugrenzen, wonach Sie suchen. Leider fehlt die chart.js-Dokumentation in einigen der fortgeschritteneren Beispiele, die sicherlich eine große Hilfe wären, um mit diesem großartigen Stück Open-Source-Code auf den neuesten Stand zu kommen.

15 Fehlerbehandlung

Während meiner ersten Testläufe bemerkte ich ein paar Mal, dass Speedtest „Cannot retrieve speedtest server list“ in der CSV-Datei berichtete. Vermutlich spiegelte dies die Zeiten wider, in denen meine Breitbandverbindung so schlecht war, dass Speedtest überhaupt keine Verbindung zum Testserver herstellen konnte. Dieser Text ist natürlich nicht in einem Format, das wir in die SQLite-Datenbank importieren wollen, also brauchte ich eine Lösung dafür, die sowohl diesen unerwünschten Text aus der CSV-Datei entfernt als auch einen Null-Eintrag in der Datenbank für den bestimmten Zeitraum enthält, da sonst jeder fehlende Eintrag in der SQL-Tabelle einfach unsichtbar wäre und auch die Ausrichtung wegwirft, die ich wollte, dass ich 24 Einträge pro Tag bei der Erstellung des Diagramms habe.

Sie werden in bandwidth.sh sehen, dass das Skript den von speedtest gesetzten Exit-Code mit der Skriptvariablen $? testet und wenn er größer als Null ist, zeigt dies an, dass der Speedtest fehlgeschlagen ist. Dies löst die Funktion aus, einen Dummy-CSV-Eintrag zu erstellen, mit dem dann die CSV-Datei für diesen Lauf überschrieben wird.

Es gibt ein paar Zeilen im Bash-Skript, die auskommentiert sind, die aber diese Fehlerroutine testen, um einen Dummy-Null-Eintrag zu erzeugen, wenn Sie das Skript ausführen, nachdem Sie diese Zeilen auskommentiert haben.

############################################
# test/debug case - forces speedtest to fail
############################################
# runTest "199999999"
# sleep 5
####### comment out after testing ##########
############################################

Dies verwendet eine’Unsinn‘-Server-ID, die Speedtest nicht mag, wodurch er einen Exit-Code ungleich Null zurückgibt. Das Skript sollte dann einen Dummy-Eintrag erstellen, der glücklich in der SQL-Tabelle stehen und ignoriert werden kann oder Sie können ihn löschen.

Eine weitere Möglichkeit, den Speedtest zum Scheitern zu bringen, besteht darin, die Netzwerkverbindung des Servers zu entfernen. Vergiss nicht, dass du das bandwidth.sh-Skript jederzeit manuell ausführen kannst, du musst nicht warten, bis der Cronjob es auslöst, obwohl du es vermeiden solltest, es manuell auszuführen, wenn ein Cronjob unmittelbar bevorsteht. Wenn zwei Skripte gleichzeitig ausgeführt werden, würde dies wahrscheinlich die CSV-Dateien und dann die SQL-Tabelle durcheinander bringen.

Wenn das Schlimmste passiert, gibt es eine Backup CSV-Datei, die als /usr/local/etc/speedtest.bak gehalten wird und die ab dem ersten Durchlauf des Bash-Skripts alle CSV-Ausgaben von Speedtest enthalten sollte. Dies kann bearbeitet werden, um unerwünschte Einträge zu entfernen, die SQL-Tabelle zu löschen und dann den gesamten Satz von CSV-Einträgen wieder in SQLite zu importieren.

16 Zeitzonen

Speedtest meldet die Zeit in UTC (im Grunde genommen ist dies dasselbe wie Greenwich Mean Time oder GMT). Die Verwendung von UTC bedeutet, dass alle in der SQL-Tabelle gespeicherten Zeiten konsistent sind und die Sommerzeit keine unerwünschten Auswirkungen hat.

Dies bedeutet jedoch, dass die Fehlerbehandlungsroutine in bandwidth.sh einen Zeitstempel für den Dummy-Eintrag erstellen muss, um dies zu reflektieren. Das ist ziemlich einfach – wir haben gerade das –utc-Flag hinzugefügt:

local RIGHTNOW=$(date --utc +%Y-%m-%dT%H:%M:%SZ)

Wenn Sie möchten, dass die x-Achsenbeschriftungen des Graphen Zeiten als etwas anderes als UTC/GMT anzeigen, dann wäre der beste Ort für eine solche Änderung die SQL SELECT-Anweisung, z.B:

strftime("%H:%M", time(times, 'localtime')) || " " || strftime("%d/%m/%Y", times) AS timestamp

Dies würde die Zeitzoneneinstellungen Ihres Linux-Servers verwenden, um die im Diagramm angezeigten Zeiten anzupassen. Oder Sie könnten einsteigen, um herauszufinden, wie Globalize dies im Frontend tun könnte.

Weitere Informationen finden Sie unter https://www.timeanddate.com/time/gmt-utc-time.html und http://www.tutorialspoint.com/sqlite/sqlite_date_time.htm

17 Weitere Schritte und Ideen

Speedtest muss nicht die Quelle Ihrer Rohdaten sein – es könnte von überall kommen und für alles sein, nicht nur für die Internetgeschwindigkeit. Das Prinzip ist das gleiche – ein Backend-Server-Prozess, der die Rohdaten in einem nützlichen Format abrufen und dann aus einem Bash-Skript mit einem Frontend in SQLite importieren kann, das die gewünschte Teilmenge von Daten extrahiert und dann grafisch darstellt.

Ein komplexeres Bash-Skript könnte Daten direkt in eine SQL-Tabelle schreiben (mit dem Befehl SQL INSERT), wenn die Formatierung als CSV nicht möglich ist. Wenn Sie die SQL-Tabelle entwerfen, denken Sie darüber nach, wie Sie die Daten später extrahieren können.

Wenn Sie Änderungen vornehmen, berücksichtigen Sie den Kontext des Codes, den Sie bearbeiten, d.h. wir haben SQL-Anweisungen in PHP-Skripten in Javascript in HTML. Merke dir das Level, auf dem du dich befindest, und kodiere entsprechend. Es kann leicht sein, den Überblick zu verlieren und am Ende PHP-Code in Javascript zu schreiben! Ich kann garantieren, dass das nicht funktioniert.

Hier sind einige Ideen für weitere Erkundungen:

  • toLocaleString ist nicht konsistent in allen Browsern implementiert. Verwenden Sie Globalize und verarbeiten Sie damit alle Zahlen-, Datums- und Zeitzonenformate.
  • Checkout httpstat (es gibt eine Bash-Skript-Version), die es ermöglicht, verschiedene Arten von Internetverbindungsdaten zu sammeln. Speichern Sie dies in einer (separaten) SQL-Tabelle und grafisieren Sie die Ausgabe.
  • Verbessern Sie das bandwidth.php Frontend, um dem Benutzer die Wahl zwischen verschiedenen Optionen zu ermöglichen: 24, 48, 72 Stunden; wählen Sie ein bestimmtes Datum, schließen Sie Upload- und Download-Daten ein, Ping-Zeiten.
  • Konvertieren Sie das HTML, um reaktionsschnellen Bootstrap-Code zu verwenden, so dass er auf verschiedenen Geräten oder Bildschirmgrößen gut läuft.
  • Erkunden Sie einige der anderen Optionen von chart.js; vielleicht ein kombiniertes Linien- und Balkendiagramm; ändern Sie die Farben und Balkengröße.
  • SQLite durch MySQL ersetzen und mehr Sicherheit durch Lesezugriff von PHP über Benutzer/Passwort erreichen.
  • Bauen Sie ein ähnliches System auf, aber mit node.js.

18 Links

Das könnte dich auch interessieren …