Hoe Python/MySQL Applicatie Session Handling te versnellen met Redis Op Ubuntu 22.04

Inleiding

Authenticatie is het proces van het verifiëren van de identiteit van gebruikers tijdens aanmeldingsverzoeken. Bij een authenticatieproces geven gebruikers hun referenties op als gebruikersnamen en wachtwoorden. Vervolgens vergelijkt de applicatie die aanmeldingsgegevens met opgeslagen databasevermeldingen. De applicatie verleent gebruikers toegang tot het systeem als er een overeenkomst is.

Het opslaan van aanmeldingsgegevens in een relationele database zoals MySQL of PostgreSQL zonder een cache-mechanisme is nog steeds een veelvoorkomende en praktische aanpak, maar het heeft de volgende beperkingen:

  • Overbelasting van de database. De applicatie moet telkens wanneer een gebruiker een aanmeldingsverzoek indient, een reis naar de databaseserver maken om de referenties van de gebruikers te verifiëren aan de hand van een database-tabel. Omdat de database mogelijk nog andere lees/schrijfverzoeken verwerkt, overbelast het hele proces de database en maakt het traag.

  • Traditionele op schijf gebaseerde databases hebben schaalbaarheidsproblemen. Wanneer uw applicatie duizenden verzoeken per seconde ontvangt, presteren op schijf gebaseerde databases niet optimaal.

Om deze uitdagingen te overwinnen, kunt u Redis gebruiken om de inloggegevens van gebruikers in de cache op te slaan, zodat uw applicatie niet bij elke inlogverzoek contact hoeft op te nemen met de backend-database. Redis is een van de meest populaire ultrasnelle gegevensopslagplaatsen die gebruikmaakt van het RAM-geheugen van uw computer om gegevens op te slaan in key-value-paren. In deze handleiding gebruikt u de Redis-database om de sessieafhandeling in uw Python/MySQL-applicatie op de Ubuntu 22.04-server te versnellen.

Vereisten

Voordat u met deze tutorial begint, moet u het volgende instellen:

Stap 1 — Het installeren van Python Database Drivers voor Redis en MySQL

Deze applicatie slaat permanent gebruikersreferenties op, zoals namen en wachtwoorden, in een MySQL-database. Wanneer een gebruiker inlogt op de applicatie, wordt er een Python-script uitgevoerd dat de MySQL-database bevraagt en de gegevens vergelijkt met opgeslagen waarden. Vervolgens worden de inloggegevens van de gebruiker gecachet in een Redis-database om toekomstige verzoeken te verwerken. Om deze logica te voltooien, hebben uw Python-scripts database-drivers (Python-modules) nodig om te communiceren met de MySQL- en Redis-servers. Volg de onderstaande stappen om de drivers te installeren:

  1. Werk uw pakketinformatie-index bij en voer de volgende opdracht uit om python3-pip te installeren, een Python-pakketbeheerder waarmee u aanvullende modules kunt installeren die geen onderdeel zijn van de Python-standaardbibliotheek.
sudo apt install python3-pip
  1. Installeer de MySQL-driver voor Python:
pip install mysql-connector-python
  1. Installeer de Redis-driver voor Python:
pip install redis

Na het installeren van de benodigde drivers voor communicatie met MySQL en Redis, ga verder met de volgende stap en initialiseer een MySQL-database.

Stap 2 – Instellen van een voorbeeld MySQL-database

Voor deze handleiding heeft u één MySQL-tabel nodig. In een productieomgeving kunt u tientallen tabellen hebben die andere verzoeken afhandelen. Stel een database in en maak de tabel aan door de volgende opdrachten uit te voeren:

  1. Meld u aan bij de MySQL-databaseserver als gebruiker root:

    sudo mysql -u root -p
    
  2. Voer uw wachtwoord voor de MySQL-server van root in wanneer daarom wordt gevraagd en druk op ENTER om door te gaan. Voer vervolgens de volgende opdracht uit om een voorbeelddatabase company en een gebruikersaccount company_user aan te maken. Vervang example-mysql-password door een sterk wachtwoord:

  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. Zorg ervoor dat u de volgende uitvoer ontvangt om te bevestigen dat de vorige opdrachten succesvol zijn uitgevoerd:

    Uitvoer
    Query OK, 1 rij beïnvloed (0.01 sec)
  2. Schakel over naar de nieuwe company database:

    1. USE company;
  3. Bevestig dat u bent verbonden met de nieuwe database door de volgende uitvoer te controleren:

    Uitvoer
    Database gewijzigd
  4. Maak een tabel genaamd system_users. De kolom user_id dient als PRIMARY KEY om elke gebruiker uniek te identificeren. De kolommen username en password zijn de inloggegevens die gebruikers moeten invoeren om in te loggen op de applicatie. De kolommen first_name en last_name slaan de namen van de gebruikers op:

    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;
    
  5. Zorg ervoor dat je de nieuwe tabel hebt aangemaakt door de volgende output te verifiëren:

    Output
    Query OK, 0 rijen beïnvloed (0.03 sec)
  6. Vul de tabel system_users met voorbeeldgegevens. Gebruik de ingebouwde MySQL-functie MD5(...) om het wachtwoord te hashen voor beveiligingsdoeleinden:

    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'));
  7. Controleer de output hieronder:

    Output
    Query OK, 1 rij beïnvloed (0.00 sec)
  8. Voer de volgende query uit op de system_users tabel om te controleren of de gegevens aanwezig zijn:

    1. SELECT
    2. user_id,
    3. first_name,
    4. last_name,
    5. password
    6. FROM system_users;
  9. Verifieer de volgende output:

    Output
    +---------+------------+-----------+----------------------------------+ | user_id | first_name | last_name | password | +---------+------------+-----------+----------------------------------+ | 1 | JOHN | DOE | 57210b12af5e06ad2e6e54a93b1465aa | | 2 | MARY | HENRY | 259640f97ac2b4379dd540ff4016654c | | 3 | PETER | JADE | 48ef85c894a06a4562268de8e4d934e1 | +---------+------------+-----------+----------------------------------+ 3 rijen in set (0.00 sec)
  10. Meld u af bij de MySQL-database:

    1. QUIT;

U hebt nu de juiste MySQL-database voor uw toepassing ingesteld. In de volgende stap bouwt u een Python-module die communiceert met uw voorbeelddatabase.

Stap 3 – Het maken van een centrale MySQL Gateway-module voor Python

Wanneer u een Python-project codeert, moet u een afzonderlijke module maken voor elke taak om code-herbruikbaarheid te bevorderen. In deze stap stelt u een centrale module in die u in staat stelt om verbinding te maken met en query’s uit te voeren op de MySQL-database vanuit een Python-script. Volg de onderstaande stappen:

  1. Maak een project directory aan. Deze directory scheidt uw Python-broncodebestanden van de rest van de systeembestanden:

    1. mkdir project
  2. Schakel over naar de nieuwe project map:

    1. cd project
  3. Gebruik de teksteditor nano om een nieuw bestand mysql_db.py te openen. Dit bestand bevat de Python-module die communiceert met de MySQL-database:

    nano mysql_db.py
    
  4. Voer de volgende informatie in het bestand mysql_db.py in. Vervang example-mysql-password door het juiste MySQL-wachtwoord voor het account company_user:

    ~/project/mysql_db.py
    
    importeer mysql.connector
    
    klasse MysqlDb:
    
    def db_con(zelf):
    
        mysql_con = mysql.connector.connect(
            host     = "localhost",
            gebruiker     = "company_user",
            wachtwoord = "example-mysql-password",
            database = "company",
            poort     = "3306"
        )
    
        return mysql_con
    
    def query(zelf, gebruikersnaam, wachtwoord):
    
        db = zelf.db_con()
        db_cursor = db.cursor()
    
        db_query  = "select gebruikersnaam, wachtwoord from systeem_gebruikers where gebruikersnaam = %s and wachtwoord = md5(%s)"
        db_cursor.execute(db_query, (gebruikersnaam, wachtwoord))
    
        resultaat = db_cursor.fetchone()
        rij_telling = db_cursor.rowcount
    
        als  rij_telling < 1:
            geef False
        anders:
            geef resultaat[1]
    
  5. Sla het bestand mysql_db.py op en sluit het.

Het modulebestand mysql_db.py bevat één klasse (MysqlDb:) met twee methoden:
db_con(self):, maakt verbinding met de eerder aangemaakte voorbeelddatabase company en retourneert een herbruikbare MySQL-verbinding met de verklaring return mysql_con.
query(self, username, password):, een methode die een username en password accepteert en de tabel system_users doorzoekt om te controleren of er een overeenkomst is. De voorwaardelijke verklaring if row_count < 1: ... else: return result[1] retourneert de boolean-waarde False als een gebruiker niet bestaat in de tabel, of het wachtwoord van de gebruiker (result[1]) als de toepassing een overeenkomst vindt.

Met de MySQL-module gereed, volgt u de volgende stap om een vergelijkbare Redis-module op te zetten die communiceert met de Redis key-value store.

Stap 4 – Het maken van een centrale Redis-module voor Python

In deze stap schrijft u een module die verbinding maakt met de Redis-server. Voer de volgende stappen uit:

  1. Open een nieuw redis_db.py bestand:

    nano redis_db.py
    
  2. Voer de volgende informatie in het bestand redis_db.py in. Vervang example-redis-password door het juiste wachtwoord voor de Redis-server:

    ~/project/redis_db.py
    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
    
  3. Sla het bestand redis_db.py op en sluit het.

  • Het bovenstaande bestand heeft één klasse (RedisDb:).

  • Onder deze klasse gebruikt de methode db_con(self): de verstrekte referenties om verbinding te maken met de Redis-server en geeft een herbruikbare verbinding terug met de instructie return redis_con.

Na het instellen van de Redis-klasse, maak het hoofdbestand voor je project in de volgende stap.

Stap 5 – Het invoerpunt van de applicatie maken

Elke Python-applicatie moet een invoerpunt hebben, oftewel het hoofdbestand dat wordt uitgevoerd wanneer de applicatie wordt gestart. In dit bestand maak je code die de huidige tijd van de server weergeeft voor geauthenticeerde gebruikers. Dit bestand maakt gebruik van de aangepaste MySQL- en Redis-modules die je hebt gemaakt om gebruikers te authenticeren. Volg de onderstaande stappen om het bestand te maken:

  1. Open een nieuw bestand genaamd index.py:

    nano index.py
  2. Voer de volgende informatie in het bestand index.py in:

    ~/project/index.py
    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": "Ongeldige gebruikersnaam/wachtwoord."}
                else:
                    resp = {"time": current_time, "geautoriseerd door": "Redis server"}
            else:
                mysql_resp  = mysql_server.query(auth_user, auth_password)
                if mysql_resp == False:
                     resp =  {"error": "Ongeldige gebruikersnaam/wachtwoord."}
                else:
                    resp = {"time": current_time, "geautoriseerd door": "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("Webserver draait op poort 8080...")
    
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        httpd.server_close()
        print("Webserver is gestopt met draaien.")
    
  3. Sla het bestand index.py op en sluit het.

  • In het bestand index.py voegt de import... sectie de volgende modules toe aan je project:

    • utf_8, base64, md5 en json, tekstcodering en opmaakmodules.

    • http.server, HTTPStatus en socketserver, webservermodules.

    • datetime, tijd-/datummodule.

    • mysql_db en redis_db, aangepaste modules die je eerder hebt gemaakt om toegang te krijgen tot de MySQL- en Redis-servers.

  • De HttpHandler (http.server.SimpleHTTPRequestHandler): is een handlerklasse voor de HTTP-server. Onder de klasse dient de methode do_GET(self): de HTTP-GET-verzoeken en geeft de datum/tijd van het systeem weer voor geauthenticeerde gebruikers.

  • In de logica van if ... : else: ... voert het Python-script de logische verklaring if redis_client.exists(auth_user): uit om te controleren of de referenties van de gebruiker bestaan in de Redis-server. Als de gebruikersgegevens bestaan en het opgeslagen wachtwoord in Redis niet overeenkomt met het ingediende wachtwoord van de gebruiker, geeft de applicatie de foutmelding {"error": "Ongeldige gebruikersnaam/wachtwoord."} terug.

Als de gebruikersgegevens niet bestaan in de Redis-server, raadpleegt de applicatie de MySQL-database door de instructie mysql_resp = mysql_server.query(auth_user, auth_password) te gebruiken. Als het door de gebruiker ingevoerde wachtwoord niet overeenkomt met de in de database opgeslagen waarde, geeft de applicatie de foutmelding {"error": "Ongeldige gebruikersnaam/wachtwoord."} terug. Anders worden de gebruikersgegevens in de Redis-server gecachet met behulp van de instructie redis_client.set(auth_user, mysql_resp).

  • In alle gevallen waarin de referenties van de gebruiker overeenkomen met de Redis/MySQL gegevens, toont de applicatie de huidige datum/tijd van het systeem met behulp van de {"time": huidige_tijd, ...} verklaring. De invoer geautoriseerd door in de output stelt u in staat om de database server te zien die de gebruikers in de applicatie authenticeren.

      if redis_client.exists(auth_user):
          if md5(auth_password.encode('utf8')).hexdigest() != redis_client.get(auth_user).decode('utf8'):
              resp = {"error": "Ongeldige gebruikersnaam/wachtwoord."}
          else:
              resp = {"time": huidige_tijd, "geautoriseerd door": "Redis server"}
      else:
          mysql_resp  = mysql_server.query(auth_user, auth_password)
          if mysql_resp == False:
               resp =  {"error": "Ongeldige gebruikersnaam/wachtwoord."}
          else:
              resp = {"time": huidige_tijd, "geautoriseerd door": "MySQL server"}
              redis_client.set(auth_user, mysql_resp)   
    

U heeft nu het hoofdbestand voor de applicatie ingesteld. In de volgende stap gaat u de applicatie testen.

Stap 6 – De applicatie testen

In deze stap voert u uw applicatie uit om te zien of het Redis-cachemechanisme werkt. Voer de volgende commando’s uit om de applicatie te testen:

  1. Gebruik de volgende python3 opdracht om de applicatie uit te voeren:

    python3 index.py
    
  2. Zorg ervoor dat de aangepaste webserver van de applicatie actief is:

    Uitvoer
    Webserver draait op poort 8080...
  3. Maak een nieuwe SSH-verbinding met uw server in een nieuw terminalvenster en voer de volgende curl-opdrachten uit om vier GET-verzoeken te sturen met behulp van de referenties van john_doe. Voeg [1-4] toe aan het einde van de URL http://localhost:8080/ om de vier verzoeken in één opdracht te verzenden:

    curl -X GET -u john_doe:password_1  http://localhost:8080/[1-4]
    
  4. Verifieer de volgende uitvoer. De MySQL-server bedient alleen het eerste authenticatieverzoek. Vervolgens bedient de Redis-database de volgende drie verzoeken.

    Uitvoer
    [1/4] { "tijd": "2023-11-07 10:04:38", "geautoriseerd door": "MySQL-server" } [4/4] { "tijd": "2023-11-07 10:04:38", "geautoriseerd door": "Redis-server" }

Uw toepassingslogica werkt nu zoals verwacht.

Conclusie

In deze handleiding heb je een Python-applicatie gebouwd die de Redis-server gebruikt om gebruikersinloggegevens te cachen. Redis is een zeer beschikbare en schaalbare databaseserver die duizenden transacties per seconde kan uitvoeren. Met het Redis-cachemechanisme in je applicatie kun je het verkeer naar je backend-databaseserver aanzienlijk verminderen. Voor meer informatie over Redis-toepassingen kun je verwijzen naar onze Redis-zelfstudies.

Source:
https://www.digitalocean.com/community/tutorials/how-to-speed-up-python-mysql-application-session-handling-with-redis-on-ubuntu-22-04