Echtzeit-Wasserabrechnung mit PHP und Redis® Pub/Sub entwickeln
Redis® Pub/Sub ist ein Architekturmodell, das es Publishern ermöglicht, Daten über definierte Kanäle an Subscriber zu senden. Diese Technologie eignet sich besonders für Echtzeitanwendungen, die auf Interprozesskommunikation (IPC) basieren – zum Beispiel Finanzplattformen oder Chatsysteme. In solchen Szenarien ist minimale Latenz entscheidend, da Nutzer eine sofortige Übermittlung von Nachrichten erwarten. Klassische SQL-Datenbanken stoßen hier schnell an ihre Grenzen. Redis® hingegen nutzt den Arbeitsspeicher des Servers und kann extrem viele Lese-/Schreibvorgänge pro Sekunde ausführen.
Ein weiterer Vorteil von Redis® in Kombination mit dem Pub/Sub-Prinzip ist die Entkopplung von Publishern und Subscribern. Daten können einfach an einen Kanal gesendet werden, ohne dass die Empfänger definiert sein müssen. Gleichzeitig können Subscriber beliebige Kanäle abonnieren, ohne die Absender kennen zu müssen.
In solch entkoppelten Systemen können Frontend- und Backend-Teams unabhängig voneinander entwickeln und testen. Dadurch verkürzt sich die Entwicklungszeit, die Entwickler erhalten mehr Freiheiten und die Komplexität sinkt – was auch die Rekrutierung erleichtert.
In dieser Anleitung erstellst du ein modulares Wasserabrechnungssystem mit PHP und Redis®. Dabei übernimmt das Frontend (als Publisher) die Erfassung der Verbrauchsdaten und sendet diese an einen Abrechnungskanal. Das Backend (als Subscriber) verarbeitet die Daten und speichert sie dauerhaft in einer MySQL-Datenbank.
Voraussetzungen
Um dieser Redis® Pub/Sub-Anleitung folgen zu können, benötigst du:
- einen Server mit Ubuntu 20.04
- einen Benutzer mit sudo-Rechten
- einen installierten LAMP-Stack
- einen Redis®-Server
1. PHP Redis®-Erweiterung installieren
Damit PHP mit Redis® kommunizieren kann, installiere die Erweiterung php-redis
. Öffne ein Terminal, verbinde dich via SSH mit dem Server und aktualisiere die Paketquellen:
$ sudo apt update
Installiere nun die Erweiterung:
$ sudo apt install -y php-redis
Starte Apache neu, um die Änderungen zu aktivieren:
$ sudo systemctl restart apache2
Die Umgebung ist nun bereit – als Nächstes wird die Datenbank eingerichtet.
2. Beispieldatenbank und Benutzer einrichten
Redis® speichert Informationen im RAM – ideal für schnelle, temporäre Datenverarbeitung. In diesem Wasserabrechnungssystem nimmt Redis® die Verbrauchsdaten entgegen, die anschließend dauerhaft in MySQL gespeichert werden.
Melde dich als root-Benutzer bei MySQL an:
$ sudo mysql -u root -p
Gib dein root-Passwort ein. Führe dann folgende Befehle aus, um die Datenbank billing_db
samt Benutzer anzulegen (ersetze EXAMPLE_PASSWORD
durch ein sicheres Passwort):
mysql> CREATE DATABASE billing_db;
CREATE USER 'billing_db_user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'EXAMPLE_PASSWORD';
GRANT ALL PRIVILEGES ON billing_db.* TO 'billing_db_user'@'localhost';
FLUSH PRIVILEGES;
Wechsle in die neu erstellte Datenbank:
mysql> USE billing_db;
Erstelle nun die Tabelle customers
für Kundendaten:
mysql> CREATE TABLE customers (
customer_id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
first_name VARCHAR(50),
last_name VARCHAR(50)
) ENGINE = InnoDB;
Füge Beispieldaten ein:
mysql> INSERT INTO customers (first_name, last_name) values ('JOHN', 'DOE');
INSERT INTO customers (first_name, last_name) values ('MARY', 'SMITH');
INSERT INTO customers (first_name, last_name) values ('STEVE', 'JONES');
Jetzt folgt die Tabelle billings
, um monatliche Abrechnungen zu speichern:
mysql> CREATE TABLE billings (
ref_id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
customer_id BIGINT,
billing_period VARCHAR(50),
units_consumed DOUBLE,
cost_per_unit DOUBLE
) ENGINE = InnoDB;
Beende die MySQL-Sitzung:
mysql> QUIT;
Die Datenbank samt Tabellenstruktur und Testdaten ist nun bereit. Im nächsten Schritt wird das Frontend-Skript zur Erfassung der Verbrauchsdaten erstellt.
3. PHP-Frontend-Skript entwickeln
Der Wasserverbrauch wird in Kubikmetern erfasst. Falls keine intelligenten Zähler installiert sind, lesen Außendienstmitarbeiter die Werte manuell ab und übermitteln sie an das Backend.
Heute lässt sich dies auch über mobile Apps realisieren, die via API mit einem Server kommunizieren. Erstelle nun ein Frontend-Skript in PHP, das solche Daten verarbeitet. Öffne dazu die Datei frontend.php
im Root-Verzeichnis des Webservers:
$ sudo nano /var/www/html/frontend.php
Füge folgenden Code ein:
<?php
try {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$channel = 'billings';
$billing_data = file_get_contents('php://input');
$redis->publish($channel, $billing_data);
echo "Data successfully sent to the billings channel.\n";
} catch (Exception $e) {
echo $e->getMessage();
}
Dieses Skript:
- verbindet sich mit Redis® auf
127.0.0.1
- liest eingehende JSON-Daten
- sendet sie an den
billings
-Kanal - gibt bei Erfolg eine Bestätigung aus oder fängt Fehler mit
try/catch
ab
Damit ist dein Frontend fertig eingerichtet – im nächsten Schritt folgt das Backend.
4. PHP-Backend-Skript erstellen
Um eingehende Verbrauchsdaten weiterzuverarbeiten und dauerhaft in MySQL zu speichern, abonnierst du mit einem PHP-Skript namens backend.php
den billings
-Kanal. Erstelle die Datei mit folgendem Befehl:
$ sudo nano /var/www/html/backend.php
Füge dann folgenden Code in die Datei ein:
<?php
try {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis->setOption(Redis:: OPT_READ_TIMEOUT, -1);
$redis->subscribe(['billings'], function($instance, $channelName, $message) {
$billing_data = json_decode($message, true);
$db_name = 'billing_db';
$db_user = 'billing_db_user';
$db_password = 'EXAMPLE_PASSWORD';
$db_host = 'localhost';
$pdo = new PDO('mysql:host=' . $db_host . '; dbname=' . $db_name, $db_user, $db_password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = 'insert into billings(
customer_id,
billing_period,
units_consumed,
cost_per_unit
)
values(
:customer_id,
:billing_period,
:units_consumed,
:cost_per_unit
)';
$data = [];
$data = [
'customer_id' => $billing_data['customer_id'],
'billing_period' => $billing_data['billing_period'],
'units_consumed' => $billing_data['units_consumed'],
'cost_per_unit' => 2.5
];
$stmt = $pdo->prepare($sql);
$stmt->execute($data);
echo "The Redis® data was sent to the MySQL database successfully.\n" ;
});
} catch (Exception $e) {
echo $e->getMessage();
}
Dieses Backend-Skript:
- stellt eine Verbindung zu Redis® her
- abonniert den
billings
-Kanal - deaktiviert das Timeout, sodass der Listener unbegrenzt läuft
- verarbeitet eingehende JSON-Daten
- speichert die Informationen über ein vorbereitetes SQL-Statement in MySQL
5. Redis® Pub/Sub Anwendung testen
Öffne zwei Terminalfenster. Im ersten Fenster startest du den Backend-Listener mit folgendem Befehl:
$ php /var/www/html/backend.php
Hinweis: Das Skript bleibt aktiv und wartet dauerhaft auf neue Nachrichten.
Im zweiten Terminal kannst du über folgende curl
-Befehle Testdaten an das Frontend senden:
$ curl -X POST http://localhost/frontend.php -H 'Content-Type: application/json' -d '{"customer_id":1, "billing_period": "2021-08-31", "units_consumed":12.36}'
$ curl -X POST http://localhost/frontend.php -H 'Content-Type: application/json' -d '{"customer_id":2, "billing_period": "2021-08-31", "units_consumed":40.20}'
$ curl -X POST http://localhost/frontend.php -H 'Content-Type: application/json' -d '{"customer_id":3, "billing_period": "2021-08-31", "units_consumed":24.36}'
Bei erfolgreichem Versand sollte die Rückmeldung lauten:
Daten erfolgreich an den billings-Kanal gesendet.
Im ersten Terminalfenster mit laufendem Backend-Skript sollte Folgendes erscheinen:
Die Redis®-Daten wurden erfolgreich in die MySQL-Datenbank übertragen.
Um zu überprüfen, ob die Daten in MySQL angekommen sind, melde dich mit folgendem Befehl an:
$ mysql -u billing_db_user -p
Gib dein Passwort (z. B. EXAMPLE_PASSWORD
) ein. Wechsle dann zur Datenbank:
mysql> USE billing_db;
Erwartete Rückmeldung:
Database changed.
Führe nun folgende SQL-Abfrage aus, um alle Abrechnungen samt Kundendaten anzuzeigen:
mysql> SELECT
billings.ref_id as bill_reference_no,
customers.customer_id,
customers.first_name,
customers.last_name,
billings.billing_period,
billings.units_consumed,
billings.cost_per_unit,
CONCAT('$', (billings.units_consumed * billings.cost_per_unit)) as bill_amount
FROM billings
LEFT JOIN customers
ON billings.customer_id = customers.customer_id;
Die Ausgabe sollte in etwa wie folgt aussehen:
+-------------------+-------------+------------+-----------+----------------+----------------+---------------+-------------+ | bill_reference_no | customer_id | first_name | last_name | billing_period | units_consumed | cost_per_unit | bill_amount | +-------------------+-------------+------------+-----------+----------------+----------------+---------------+-------------+ | 1 | 1 | JOHN | DOE | 2021-08-31 | 12.36 | 2.5 | $30.9 | | 2 | 2 | MARY | SMITH | 2021-08-31 | 40.2 | 2.5 | $100.5 | | 3 | 3 | STEVE | JONES | 2021-08-31 | 24.36 | 2.5 | $60.9 | +-------------------+-------------+------------+-----------+----------------+----------------+---------------+-------------+ 3 rows in set (0.00 sec)
Fazit
In dieser Anleitung hast du gezeigt, wie man mit Redis® Pub/Sub in Verbindung mit PHP eine Echtzeitverarbeitung von Wasserverbrauchsdaten realisieren kann. Die Daten wurden erfolgreich aus Redis® entgegengenommen und automatisch in einer MySQL-Datenbank gespeichert – ein anschauliches Beispiel für die Entkopplung von Systemkomponenten durch Redis®.