Website-Suche

Wie man die Sitzungsverwaltung von Python/MySQL-Anwendungen mit Redis auf Ubuntu 22.04 beschleunigt


Der Autor wählte die Apache Software Foundation, um eine Spende im Rahmen des Write for DOnations-Programms zu erhalten.

Einführung

Die Authentifizierung ist der Prozess der Überprüfung der Identität von Benutzern während Anmeldeanfragen. In einem Authentifizierungsprozess reichen die Benutzer ihre Anmeldeinformationen in Form von Benutzernamen und Passwörtern ein. Dann vergleicht die Anwendung diese Anmeldeinformationen mit gespeicherten Datenbankeinträgen. Die Anwendung gewährt den Benutzern Zugang zum System, wenn eine Übereinstimmung vorliegt.

Das Speichern von Anmeldeinformationen in einer relationalen Datenbank wie MySQL oder PostgreSQL ohne einen Caching-Mechanismus ist nach wie vor ein gängiger und praktischer Ansatz, bringt jedoch die folgenden Einschränkungen mit sich:

  • Überlastung der Datenbank. Die Anwendung muss jedes Mal, wenn ein Benutzer eine Anmeldeanfrage sendet, eine Verbindung zum Datenbankserver herstellen, um die Anmeldeinformationen der Benutzer aus einer Datenbanktabelle zu überprüfen. Da die Datenbank möglicherweise auch andere Lese-/Schreibanforderungen bearbeitet, überlastet der gesamte Prozess die Datenbank und macht sie langsamer.

  • Traditionelle, datenträgerbasierte Datenbanken haben Skalierungsprobleme. Wenn Ihre Anwendung Tausende von Anfragen pro Sekunde erhält, arbeiten datenträgerbasierte Datenbanken nicht optimal.

Um die oben genannten Herausforderungen zu überwinden, können Sie Redis verwenden, um die Anmeldedaten der Benutzer zwischenzuspeichern, sodass Ihre Anwendung nicht bei jeder Anmeldeforderung mit der Backend-Datenbank kommunizieren muss. Redis ist eines der beliebtesten ultran schnellen Datenspeicher, das den RAM Ihres Computers nutzt, um Daten in Schlüssel-Wert-Paaren zu speichern. In diesem Handbuch werden Sie die Redis-Datenbank verwenden, um die Sitzungsverwaltung in Ihrer Python/MySQL-Anwendung auf dem Ubuntu 22.04-Server zu beschleunigen.

Voraussetzungen

Bevor Sie mit diesem Tutorial beginnen, müssen Sie Folgendes einrichten:

  • Ein Ubuntu 22.04-Server bereitstellen.

  • Konfigurieren und initialisieren Sie den Server.

  • Einen Nicht-Root sudo Benutzer erstellen.

  • Wechseln Sie zum neuen sudo Benutzerkonto und installieren Sie:

    • MeinSQL-Server.

    • Redis-Server.

Schritt 1 — Installation der Python-Datenbanktreiber für Redis und MySQL

Diese Anwendung speichert die Anmeldedaten der Benutzer, wie Namen und Passwörter, dauerhaft in einem MySQL-Datenbankserver. Wenn sich ein Benutzer in der Anwendung anmeldet, fragt ein Python-Skript die MySQL-Datenbank ab und vergleicht die Details mit den gespeicherten Werten. Dann cached das Python-Skript die Anmeldedaten des Benutzers in einer Redis-Datenbank, um andere zukünftige Anfragen zu bedienen. Um diese Logik abzuschließen, benötigen Ihre Python-Skripte Datenbanktreiber (Python-Module), um mit den MySQL- und Redis-Servern zu kommunizieren. Befolgen Sie die folgenden Schritte, um die Treiber zu installieren:

  1. Aktualisieren Sie Ihr Paketinformations-Index und führen Sie den folgenden Befehl aus, um python3-pip zu installieren, einen Python-Paketmanager, mit dem Sie zusätzliche Module installieren können, die nicht Teil der Python-Standardbibliothek sind.
sudo apt install python3-pip
  1. Installieren Sie den MySQL-Treiber für Python:
pip install mysql-connector-python
  1. Installieren Sie den Redis-Treiber für Python:
pip install redis

Nachdem Sie die erforderlichen Treiber zur Kommunikation mit MySQL und Redis installiert haben, fahren Sie mit dem nächsten Schritt fort und initialisieren Sie eine MySQL-Datenbank.

Schritt 2 — Einrichten einer Beispiel-MySQL-Datenbank

Für diesen Leitfaden benötigen Sie eine MySQL-Tabelle. In einer Produktionsumgebung können Sie Dutzende von Tabellen haben, die anderen Anforderungen dienen. Richten Sie eine Datenbank ein und erstellen Sie die Tabelle, indem Sie die folgenden Befehle ausführen:

  1. Melden Sie sich als root Benutzer am MySQL-Datenbankserver an:

    sudo mysql -u root -p
    
  2. Geben Sie das root Passwort Ihres MySQL-Servers ein, wenn Sie dazu aufgefordert werden, und drücken Sie ENTER, um fortzufahren. Führen Sie dann den folgenden Befehl aus, um eine Beispiel-company Datenbank und ein company_user Konto zu erstellen. Ersetzen Sie example-mysql-password durch ein sicheres Passwort:

  1. CREATE DATABASE company;
  2. CREATE USER 'company_user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'example-mysql-password';
  3. GRANT ALL PRIVILEGES ON company.* TO 'company_user'@'localhost';
  4. FLUSH PRIVILEGES;
  1. Stellen Sie sicher, dass Sie folgende Ausgabe erhalten, um zu bestätigen, dass die vorherigen Befehle erfolgreich ausgeführt wurden:

    Query OK, 1 row affected (0.01 sec)
    
  2. Wechseln Sie zur neuen company Datenbank:

  3. USE company;
  4. Bestätigen Sie, dass Sie mit der neuen Datenbank verbunden sind, indem Sie die folgende Ausgabe überprüfen:

    Database changed
    
  5. Erstellen Sie eine system_users Tabelle. Die user_id Spalte dient als PRIMARY KEY, um jeden Benutzer eindeutig zu identifizieren. Die Spalten username und password sind die Anmeldedaten, die Benutzer eingeben müssen, um sich in der Anwendung anzumelden. Die Spalten first_name und last_name speichern die Namen der Benutzer:

    custom_prefix(mysql>)
    CREATE TABLE system_users (
        user_id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
        username VARCHAR(50),
        first_name VARCHAR(50),
        last_name VARCHAR(50),
        password VARCHAR(50)
    ) ENGINE = InnoDB;
    
  6. Stellen Sie sicher, dass Sie die neue Tabelle erstellt haben, indem Sie die folgende Ausgabe überprüfen:

    Query OK, 0 rows affected (0.03 sec)
    
  7. Füllen Sie die system_users Tabelle mit Beispieldaten. Verwenden Sie die integrierte MySQL-Funktion MD5(...), um das Passwort aus Sicherheitsgründen zu hashen:

    1. INSERT INTO system_users (username, first_name, last_name, password) VALUES ('john_doe', 'JOHN', 'DOE', MD5('password_1'));
    2. INSERT INTO system_users (username, first_name, last_name, password) VALUES ('mary_henry', 'MARY', 'HENRY', MD5('password_2'));
    3. INSERT INTO system_users (username, first_name, last_name, password) VALUES ('peter_jade', 'PETER', 'JADE', MD5('password_3'));
  8. Überprüfen Sie die untenstehende Ausgabe:

    Query OK, 1 row affected (0.00 sec)
    
  9. Abfragen Sie die Tabelle system_users, um sicherzustellen, dass die Daten vorhanden sind:

    1. SELECT
    2. user_id,
    3. first_name,
    4. last_name,
    5. password
    6. FROM system_users;
  10. Überprüfen Sie die folgende Ausgabe:

  11. +---------+------------+-----------+----------------------------------+
    | user_id | first_name | last_name | password                         |
    +---------+------------+-----------+----------------------------------+
    |       1 | JOHN       | DOE       | 57210b12af5e06ad2e6e54a93b1465aa |
    |       2 | MARY       | HENRY     | 259640f97ac2b4379dd540ff4016654c |
    |       3 | PETER      | JADE      | 48ef85c894a06a4562268de8e4d934e1 |
    +---------+------------+-----------+----------------------------------+
    3 rows in set (0.00 sec)
    
  12. Von der MySQL-Datenbank abmelden:

  1. QUIT;

Sie haben jetzt die richtige MySQL-Datenbank für Ihre Anwendung eingerichtet. Im nächsten Schritt werden Sie ein Python-Modul erstellen, das mit Ihrer Musterdatenbank kommuniziert.

Schritt 3 — Erstellen eines zentralen MySQL-Gateway-Moduls für Python

Beim Programmieren eines Python-Projekts sollten Sie ein separates Modul für jede Aufgabe erstellen, um die Wiederverwendbarkeit des Codes zu fördern. In diesem Schritt richten Sie ein zentrales Modul ein, das es Ihnen ermöglicht, die MySQL-Datenbank von einem Python-Skript aus zu verbinden und abzufragen. Befolgen Sie die folgenden Schritte:

  1. Erstellen Sie ein project-Verzeichnis. Dieses Verzeichnis trennt Ihre Python-Quellcodedateien von den restlichen Systemdateien:

    1. mkdir project
  2. Wechseln Sie zum neuen project Verzeichnis:

    1. cd project
  3. Verwenden Sie den nano Texteditor, um eine neue mysql_db.py Datei zu öffnen. Diese Datei enthält das Python-Modul, das mit der MySQL-Datenbank kommuniziert:

  4. nano mysql_db.py
    
  5. Geben Sie die folgenden Informationen in die mysql_db.py Datei ein. Ersetzen Sie example-mysql-password durch das korrekte MySQL-Passwort für das company_user Konto:

  6. import mysql.connector
    
    class MysqlDb:
    
    def db_con(self):
    
        mysql_con = mysql.connector.connect(
            host     = "localhost",
            user     = "company_user",
            password = "example-mysql-password",
            database = "company",
            port     = "3306"
        )
    
        return mysql_con
    
    def query(self, username, password):
    
        db = self.db_con()
        db_cursor = db.cursor()
    
        db_query  = "select username, password from system_users where username = %s and password = md5(%s)"
        db_cursor.execute(db_query, (username, password))
    
        result = db_cursor.fetchone()
        row_count = db_cursor.rowcount
    
        if  row_count < 1:
            return False
        else:
            return result[1]
    
  7. Speichern Sie die mysql_db.py Datei und schließen Sie sie.

Die mysql_db.py Modul-Datei enthält eine Klasse (MysqlDb:) mit zwei Methoden: - db_con(self):, verbindet sich mit der Beispiel-Datenbank company, die Sie zuvor erstellt haben, und gibt eine wiederverwendbare MySQL-Verbindung mit der Anweisung return mysql_con zurück. - query(self, username, password):, eine Methode, die einen Benutzernamen und ein Passwort akzeptiert und die Tabelle system_users abfragt, um festzustellen, ob eine Übereinstimmung vorliegt. Die bedingte Anweisung if row_count < 1: ... else: return result[1] gibt den booleschen Wert False zurück, wenn ein Benutzer nicht in der Tabelle existiert oder das Passwort des Benutzers (result[1]), wenn die Anwendung eine Übereinstimmung findet.

Mit dem MySQL-Modul bereit, gehen Sie zum nächsten Schritt über, um ein ähnliches Redis-Modul einzurichten, das mit dem Redis-Key-Value-Store kommuniziert.

Schritt 4 — Erstellung eines zentralen Redis-Moduls für Python

In diesem Schritt werden Sie ein Modul programmieren, das sich mit dem Redis-Server verbindet. Führen Sie die folgenden Schritte aus:

  1. Öffne eine neue redis_db.py Datei:

  2. nano redis_db.py
    
  3. Geben Sie die folgenden Informationen in die redis_db.py-Datei ein. Ersetzen Sie example-redis-password durch das korrekte Passwort für den Redis-Server:

  4. import redis
    class RedisDb:
        def db_con(self):
            r_host = 'localhost'
            r_port = 6379
            r_pass = 'example-redis-password'
            redis_con = redis.Redis(host = r_host, port = r_port, password = r_pass)
            return redis_con
    
  5. Speichern Sie die redis_db.py Datei und schließen Sie sie.

  • Die obige Datei hat eine Klasse (RedisDb:).

  • In dieser Klasse verwendet die db_con(self):-Methode die bereitgestellten Anmeldeinformationen, um eine Verbindung zum Redis-Server herzustellen und gibt eine wiederverwendbare Verbindung mit der return redis_con-Anweisung zurück.

Nachdem Sie die Redis-Klasse eingerichtet haben, erstellen Sie im nächsten Schritt die Hauptdatei für Ihr Projekt.

Schritt 5 — Erstellen des Einstiegspunkts der Anwendung

Jede Python-Anwendung muss einen Einstiegspunkt oder eine Hauptdatei haben, die ausgeführt wird, wenn die Anwendung gestartet wird. In dieser Datei erstellen Sie einen Code, der die aktuelle Serverzeit für authentifizierte Benutzer anzeigt. Diese Datei verwendet die benutzerdefinierten MySQL- und Redis-Module, die Sie erstellt haben, um Benutzer zu authentifizieren. Folgen Sie den folgenden Schritten, um die Datei zu erstellen:

  1. Öffnen Sie eine neue index.py Datei:

  2. nano index.py
    
  3. Geben Sie die folgenden Informationen in die index.py Datei ein:

  4. from encodings import utf_8
    import base64
    from hashlib import md5
    import json
    import datetime
    import http.server
    from http import HTTPStatus
    import socketserver
    import mysql_db
    import redis_db
    
    class HttpHandler(http.server.SimpleHTTPRequestHandler):
        def do_GET(self):
            self.send_response(HTTPStatus.OK)
            self.send_header('Content-type', 'application/json')
            self.end_headers()
            authHeader = self.headers.get('Authorization').split(' ');
            auth_user, auth_password = base64.b64decode(authHeader[1]).decode('utf8').split(':')
            mysql_server = mysql_db.MysqlDb()
            redis_server = redis_db.RedisDb()
            redis_client =  redis_server.db_con()
            now = datetime.datetime.now()
            current_time = now.strftime("%Y-%m-%d %H:%M:%S")
            resp = {}
            if redis_client.exists(auth_user):
                if md5(auth_password.encode('utf8')).hexdigest() != redis_client.get(auth_user).decode('utf8'):
                    resp = {"error": "Invalid username/password."}
                else:
                    resp = {"time": current_time, "authorized by": "Redis server"}
            else:
                mysql_resp  = mysql_server.query(auth_user, auth_password)
                if mysql_resp == False:
                     resp =  {"error": "Invalid username/password."}
                else:
                    resp = {"time": current_time, "authorized by": "MySQL server"}
                    redis_client.set(auth_user, mysql_resp)
            self.wfile.write(bytes(json.dumps(resp, indent = 2) + "\r\n", "utf8"))
    httpd = socketserver.TCPServer(('', 8080), HttpHandler)
    print("Web server is running on port 8080...")
    
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        httpd.server_close()
        print("Web server has stopped runing.")
    
  5. Speichern Sie die index.py Datei und schließen Sie sie.

  • In der index.py Datei fügt der import... Abschnitt folgende Module zu Ihrem Projekt hinzu:

    • utf_8, base64, md5 und json, Textkodierungs- und Formatierungs-Module.

    • http.server, HTTPStatus und socketserver, Webserver-Module.

    • datetime, Zeit-/Datumsmodul.

    • mysql_db und redis_db, benutzerdefinierte Module, die Sie zuvor erstellt haben, um auf die MySQL- und Redis-Server zuzugreifen.

  • Die HttpHandler(http.server.SimpleHTTPRequestHandler): ist eine Handler-Klasse für den HTTP-Server. In der Klasse bedient die Methode do_GET(self): die HTTP GET Anfragen und zeigt das Datum/Zeit des Systems für authentifizierte Benutzer an.

  • In der if ... : else: ... Logik führt das Python-Skript die logische if redis_client.exists(auth_user): Anweisung aus, um zu überprüfen, ob die Anmeldedaten des Benutzers auf dem Redis-Server vorhanden sind. Wenn die Benutzerdaten vorhanden sind und das im Redis gespeicherte Passwort nicht mit dem vom Benutzer eingegebenen Passwort übereinstimmt, gibt die Anwendung den Fehler {"error": "Ungültiger Benutzername/Passwort."} zurück.

Wenn die Benutzerdaten nicht im Redis-Server vorhanden sind, fragt die Anwendung den MySQL-Datenbankserver mit der Anweisung mysql_resp =mysql_server.query(auth_user, auth_password) ab. Falls das vom Benutzer angegebene Passwort nicht mit dem im Datenbank gespeicherten Wert übereinstimmt, gibt die Anwendung den Fehler {"error": "Ungültiger Benutzername/Passwort."} zurück. Andernfalls speichert die Anwendung die Benutzerdaten im Redis-Server mit der Anweisung redis_client.set(auth_user, mysql_resp).

  • In allen Fällen, in denen die Anmeldedaten des Benutzers mit den Redis/MySQL-Details übereinstimmen, zeigt die Anwendung das aktuelle Datum/Uhrzeit des Systems an, indem die Anweisung {"time": current_time, ...} verwendet wird. Der Eintrag authorized by in der Ausgabe ermöglicht es Ihnen, den Datenbankserver zu sehen, der die Benutzer in der Anwendung authentifiziert.

  •   if redis_client.exists(auth_user):
          if md5(auth_password.encode('utf8')).hexdigest() != redis_client.get(auth_user).decode('utf8'):
              resp = {"error": "Invalid username/password."}
          else:
              resp = {"time": current_time, "authorized by": "Redis server"}
      else:
          mysql_resp  = mysql_server.query(auth_user, auth_password)
          if mysql_resp == False:
               resp =  {"error": "Invalid username/password."}
          else:
              resp = {"time": current_time, "authorized by": "MySQL server"}
              redis_client.set(auth_user, mysql_resp)   
    

Sie haben nun die Hauptdatei für die Anwendung eingerichtet. Im nächsten Schritt werden Sie die Anwendung testen.

Schritt 6 — Testen der Anwendung

In diesem Schritt werden Sie Ihre Anwendung ausführen, um zu sehen, ob der Redis-Caching-Mechanismus funktioniert. Führen Sie die folgenden Befehle aus, um die Anwendung zu testen:

  1. Verwenden Sie den folgenden python3 Befehl, um die Anwendung auszuführen:

  2. python3 index.py
    
  3. Stellen Sie sicher, dass der benutzerdefinierte Webserver der Anwendung läuft:

  4. Web server is running on port 8080...
    
  5. Stellen Sie eine weitere SSH-Verbindung zu Ihrem Server in einem neuen Terminalfenster her und führen Sie die folgenden curl-Befehle aus, um vier GET-Anfragen mit den Anmeldeinformationen von john_doe's zu senden. Fügen Sie [1-4] am Ende der http://localhost:8080/-URL hinzu, um die vier Anfragen in einem einzigen Befehl zu senden:

  6. curl -X GET -u john_doe:password_1  http://localhost:8080/[1-4]
    
  7. Überprüfen Sie die folgenden Ausgaben. Der MySQL-Server bearbeitet nur die erste Authentifizierungsanfrage. Danach bearbeitet die Redis-Datenbank die nächsten drei Anfragen.

  8. [1/4]
    {
      "time": "2023-11-07 10:04:38",
      "authorized by": "MySQL server"
    }
    [4/4]
    {
      "time": "2023-11-07 10:04:38",
      "authorized by": "Redis server"
    }
    

Ihre Anwendungslogik funktioniert jetzt wie erwartet.

Schlussfolgerung

In diesem Leitfaden haben Sie eine Python-Anwendung erstellt, die den Redis-Server verwendet, um die Anmeldedaten der Benutzer zwischenzuspeichern. Redis ist ein hochverfügbarer und skalierbarer Datenbankserver, der Tausende von Transaktionen pro Sekunde durchführen kann. Mit dem Redis-Caching-Mechanismus in Ihrer Anwendung können Sie den Datenverkehr in Ihrem Backend-Datenbankserver erheblich reduzieren. Um mehr über Redis-Anwendungen zu erfahren, konsultieren Sie unsere Redis-Tutorials.