Comment accélérer la gestion de session des applications Python/MySQL avec Redis sous Ubuntu 22.04

Introduction

L’authentification est le processus de vérification de l’identité des utilisateurs lors des demandes de connexion. Dans un processus d’authentification, les utilisateurs soumettent leurs identifiants tels que les noms d’utilisateur et les mots de passe. Ensuite, l’application compare ces identifiants de connexion avec les entrées stockées dans la base de données. L’application accorde aux utilisateurs l’accès au système s’il y a correspondance.

Stocker les identifiants de connexion dans une base de données relationnelle comme MySQL ou PostgreSQL sans mécanisme de mise en cache est encore une approche courante et pratique, mais elle présente les limitations suivantes :

  • Surcharger la base de données. L’application doit effectuer un aller-retour vers le serveur de la base de données pour vérifier les identifiants des utilisateurs à partir d’une table de la base de données chaque fois qu’un utilisateur soumet une demande de connexion. Étant donné que la base de données peut encore traiter d’autres demandes de lecture/écriture, l’ensemble du processus surcharge la base de données et le ralentit.

  • Les bases de données traditionnelles basées sur le disque présentent des problèmes de scalabilité. Lorsque votre application reçoit des milliers de demandes par seconde, les bases de données basées sur le disque ne fonctionnent pas de manière optimale.

Pour surmonter les défis mentionnés ci-dessus, vous pouvez utiliser Redis pour mettre en cache les informations d’identification de connexion des utilisateurs afin que votre application n’ait pas à contacter la base de données backend lors de chaque demande de connexion. Redis est l’un des magasins de données ultra-rapides les plus populaires qui utilise la RAM de votre ordinateur pour stocker les données sous forme de paires clé-valeur. Dans ce guide, vous utiliserez la base de données Redis pour accélérer la gestion des sessions dans votre application Python/MySQL sur le serveur Ubuntu 22.04.

Prérequis

Avant de commencer ce tutoriel, vous devrez configurer ce qui suit:

Étape 1 – Installation des pilotes de base de données Python pour Redis et MySQL

Cette application stocke définitivement les informations d’identification des utilisateurs, telles que les noms et les mots de passe, dans un serveur de base de données MySQL. Lorsqu’un utilisateur se connecte à l’application, un script Python interroge la base de données MySQL et vérifie les détails avec les valeurs enregistrées. Ensuite, le script Python met en cache les informations d’identification de connexion de l’utilisateur dans une base de données Redis pour traiter d’autres demandes futures. Pour compléter cette logique, vos scripts Python nécessitent des pilotes de base de données (modules Python) pour communiquer avec les serveurs MySQL et Redis. Suivez les étapes ci-dessous pour installer les pilotes :

  1. Mettez à jour votre index d’informations sur les paquets et exécutez la commande suivante pour installer python3-pip, un gestionnaire de paquets Python vous permettant d’installer des modules supplémentaires qui ne font pas partie de la bibliothèque standard de Python.
sudo apt install python3-pip
  1. Installez le pilote MySQL pour Python :
pip install mysql-connector-python
  1. Installez le pilote Redis pour Python :
pip install redis

Après avoir installé les pilotes nécessaires pour communiquer avec MySQL et Redis, passez à l’étape suivante et initialisez une base de données MySQL.

Étape 2 – Configuration d’une base de données MySQL d’exemple

Pour ce guide, vous avez besoin d’une table MySQL. Dans un environnement de production, vous pouvez avoir des dizaines de tables qui servent d’autres demandes. Configurez une base de données et créez la table en exécutant les commandes suivantes :

  1. Connectez-vous au serveur de base de données MySQL en tant qu’utilisateur root:

    sudo mysql -u root -p
    
  2. Entrez le mot de passe root de votre serveur MySQL lorsque vous y êtes invité, puis appuyez sur ENTRÉE pour continuer. Ensuite, exécutez la commande suivante pour créer une base de données company d’exemple et un compte company_user. Remplacez exemple-mot-de-passe-mysql par un mot de passe fort :

  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. Assurez-vous de recevoir la sortie suivante pour confirmer que les commandes précédentes ont été exécutées avec succès:

    Sortie
    Query OK, 1 row affected (0.01 sec)
  2. Passer à la nouvelle base de données company :

    1. USE company;
  3. Confirmez que vous êtes connecté à la nouvelle base de données en vérifiant la sortie suivante :

    Sortie
    Database changed
  4. Créez une table system_users. La colonne user_id sert de PRIMARY KEY pour identifier de manière unique chaque utilisateur. Les colonnes username et password sont les informations de connexion que les utilisateurs doivent soumettre pour se connecter à l’application. Les colonnes first_name et last_name stockent les noms des utilisateurs :

    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. Vérifiez que vous avez créé la nouvelle table en vérifiant la sortie suivante :

    Sortie
    Requête OK, 0 lignes affectées (0.03 sec)
  6. Remplissez la table system_users avec des données d’exemple. Utilisez la fonction MD5(...) intégrée à MySQL pour hasher le mot de passe à des fins de sécurité :

    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. Vérifiez la sortie ci-dessous:

    Sortie
    Query OK, 1 ligne affectée (0.00 sec)
  8. Interrogez la table system_users pour vous assurer que les données sont en place:

    1. SELECT
    2. user_id,
    3. first_name,
    4. last_name,
    5. password
    6. FROM system_users;
  9. Vérifiez la sortie suivante:

    Sortie
    +---------+------------+-----------+----------------------------------+ | user_id | first_name | last_name | password | +---------+------------+-----------+----------------------------------+ | 1 | JOHN | DOE | 57210b12af5e06ad2e6e54a93b1465aa | | 2 | MARY | HENRY | 259640f97ac2b4379dd540ff4016654c | | 3 | PETER | JADE | 48ef85c894a06a4562268de8e4d934e1 | +---------+------------+-----------+----------------------------------+ 3 lignes dans ensemble (0.00 sec)
  10. Déconnectez-vous de la base de données MySQL :

    1. QUIT;

Vous avez maintenant configuré la bonne base de données MySQL pour votre application. À l’étape suivante, vous allez créer un module Python qui communique avec votre base de données d’exemple.

Étape 3 – Création d’un module central de passerelle MySQL pour Python

Lors de la programmation de tout projet Python, vous devez créer un module distinct pour chaque tâche afin de favoriser la réutilisation du code. À cette étape, vous allez configurer un module central qui vous permet de vous connecter et d’interroger la base de données MySQL à partir d’un script Python. Suivez les étapes ci-dessous :

  1. Créez un répertoire project. Ce répertoire sépare vos fichiers source Python du reste des fichiers système :

    1. mkdir project
  2. Passez au nouveau répertoire project:

    1. cd project
  3. Utilisez l’éditeur de texte nano pour ouvrir un nouveau fichier mysql_db.py. Ce fichier héberge le module Python qui communique avec la base de données MySQL:

    nano mysql_db.py
    
  4. Entrez les informations suivantes dans le fichier mysql_db.py. Remplacez example-mysql-password par le mot de passe MySQL correct pour le compte company_user:

    ~/project/mysql_db.py
    
    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]
    
  5. Enregistrez et fermez le fichier mysql_db.py.

Le fichier module mysql_db.py contient une classe (MysqlDb:) avec deux méthodes :
db_con(self): se connecte à la base de données d’exemple company que vous avez créée précédemment et renvoie une connexion MySQL réutilisable à l’aide de l’instruction return mysql_con.
query(self, username, password): est une méthode qui accepte un username et un password et interroge la table system_users pour vérifier s’il y a une correspondance. L’instruction conditionnelle if row_count < 1: ... else: return result[1] renvoie la valeur booléenne False si un utilisateur n’existe pas dans la table ou le mot de passe de l’utilisateur (result[1]) si l’application trouve une correspondance.

Avec le module MySQL prêt, suivez l’étape suivante pour configurer un module Redis similaire qui communique avec le magasin de clés Redis.

Étape 4 – Création d’un module Redis central pour Python

Dans cette étape, vous allez coder un module qui se connecte au serveur Redis. Exécutez les étapes suivantes :

  1. Ouvrez un nouveau fichier redis_db.py:

    nano redis_db.py
    
  2. Entrez les informations suivantes dans le fichier redis_db.py. Remplacez example-redis-password par le mot de passe correct pour le serveur Redis :

    ~/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. Enregistrez et fermez le fichier redis_db.py.

  • Le fichier ci-dessus contient une classe (RedisDb:).

  • Sous cette classe, la méthode db_con(self): utilise les identifiants fournis pour se connecter au serveur Redis et renvoie une connexion réutilisable à l’aide de l’instruction return redis_con.

Après avoir configuré la classe Redis, créez le fichier principal de votre projet à l’étape suivante.

Étape 5 – Création du point d’entrée de l’application

Chaque application Python doit avoir un point d’entrée ou un fichier principal qui s’exécute lorsque l’application est lancée. Dans ce fichier, vous allez créer un code qui affiche l’heure actuelle du serveur pour les utilisateurs authentifiés. Ce fichier utilise les modules MySQL et Redis personnalisés que vous avez créés pour authentifier les utilisateurs. Suivez les étapes ci-dessous pour créer le fichier :

  1. Ouvrez un nouveau fichier index.py:

    nano index.py
    
  2. Entrez les informations suivantes dans le fichier index.py:

    ~/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": "Nom d'utilisateur/mot de passe incorrect."}
                else:
                    resp = {"time": current_time, "autorisé par": "Serveur Redis"}
            else:
                mysql_resp  = mysql_server.query(auth_user, auth_password)
                if mysql_resp == False:
                     resp =  {"error": "Nom d'utilisateur/mot de passe incorrect."}
                else:
                    resp = {"time": current_time, "autorisé par": "Serveur MySQL"}
                    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("Le serveur Web fonctionne sur le port 8080...")
    
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        httpd.server_close()
        print("Le serveur Web s'est arrêté de fonctionner.")
    
  3. Enregistrez et fermez le fichier index.py.

  • Dans le fichier index.py, la section import... ajoute les modules suivants à votre projet :

    • utf_8, base64, md5 et json, modules d’encodage et de formatage de texte.

    • http.server, HTTPStatus et socketserver, modules de serveur web.

    • datetime, module de temps/date.

    • mysql_db et redis_db, modules personnalisés que vous avez créés précédemment pour accéder aux serveurs MySQL et Redis.

  • Le HttpHandler(http.server.SimpleHTTPRequestHandler) est une classe de gestionnaire pour le serveur HTTP. Sous la classe, la méthode do_GET(self) sert les demandes HTTP GET et affiche la date/heure du système pour les utilisateurs authentifiés.

  • Dans la logique if ... : else: ..., le script Python exécute l’instruction logique if redis_client.exists(auth_user) pour vérifier si les informations d’identification de l’utilisateur existent dans le serveur Redis. Si les détails de l’utilisateur existent et que le mot de passe stocké dans Redis ne correspond pas au mot de passe soumis par l’utilisateur, l’application renvoie l’erreur {"error": "Nom d'utilisateur/mot de passe incorrect."}.

Si les détails de l’utilisateur n’existent pas dans le serveur Redis, l’application interroge le serveur de base de données MySQL en utilisant l’instruction mysql_resp = mysql_server.query(auth_user, auth_password). Si le mot de passe fourni par l’utilisateur ne correspond pas à la valeur stockée dans la base de données, l’application renvoie l’erreur {"error": "Nom d'utilisateur/mot de passe invalide."}. Sinon, l’application met en cache les détails de l’utilisateur dans le serveur Redis en utilisant l’instruction redis_client.set(auth_user, mysql_resp).

  • Dans tous les cas où les informations d’identification de l’utilisateur correspondent aux détails Redis/MySQL, l’application affiche la date/heure actuelle du système à l’aide de l’instruction {"time": current_time, ...}. L’entrée authorized by dans la sortie vous permet de voir le serveur de base de données qui authentifie les utilisateurs dans l’application.

      if redis_client.exists(auth_user):
          if md5(auth_password.encode('utf8')).hexdigest() != redis_client.get(auth_user).decode('utf8'):
              resp = {"error": "Nom d'utilisateur/mot de passe incorrect."}
          else:
              resp = {"time": current_time, "authorized by": "Serveur Redis"}
      else:
          mysql_resp  = mysql_server.query(auth_user, auth_password)
          if mysql_resp == False:
               resp =  {"error": "Nom d'utilisateur/mot de passe incorrect."}
          else:
              resp = {"time": current_time, "authorized by": "Serveur MySQL"}
              redis_client.set(auth_user, mysql_resp)   
    

Vous avez maintenant configuré le fichier principal de l’application. À l’étape suivante, vous testerez l’application.

Étape 6 – Tester l’application

Dans cette étape, vous exécuterez votre application pour voir si le mécanisme de mise en cache Redis fonctionne. Exécutez les commandes ci-dessous pour tester l’application:

  1. Utilisez la commande python3 suivante pour exécuter l’application:

    python3 index.py
    
  2. Assurez-vous que le serveur web personnalisé de l’application est en cours d’exécution:

    Sortie
    Le serveur web fonctionne sur le port 8080...
  3. Établissez une autre connexion SSH vers votre serveur dans une nouvelle fenêtre de terminal et exécutez les commandes curl suivantes pour envoyer quatre requêtes GET en utilisant les identifiants de john_doe. Ajoutez [1-4] à la fin de l’URL http://localhost:8080/ pour envoyer les quatre requêtes en une seule commande :

    curl -X GET -u john_doe:password_1  http://localhost:8080/[1-4]
    
  4. Vérifiez les sorties suivantes. Le serveur MySQL ne sert que la première demande d’authentification. Ensuite, la base de données Redis sert les trois demandes suivantes.

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

Votre logique d’application fonctionne maintenant comme prévu.

Conclusion

Dans ce guide, vous avez construit une application Python qui utilise le serveur Redis pour mettre en cache les informations d’identification de connexion des utilisateurs. Redis est un serveur de base de données hautement disponible et évolutif, capable d’effectuer des milliers de transactions par seconde. Grâce au mécanisme de mise en cache Redis dans votre application, vous pouvez réduire considérablement le trafic sur votre serveur de base de données backend. Pour en savoir plus sur les applications Redis, consultez nos tutoriels Redis.

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