איך להאיץ את טיפול ההפעלה של אפליקציה Python/MySQL עם Redis על Ubuntu 22.04

הקדמה

אימות הוא התהליך של אימות זהותם של משתמשים במהלך בקשות התחברות. בתהליך האימות, המשתמשים מגישים את פרטי הכניסה שלהם כשמות משתמש וסיסמאות. לאחר מכן, היישום מתאים את פרטי הכניסה הללו לרשומות מסד הנתונים השמורות. היישום מעניק גישה למשתמשים למערכת אם קיימת התאמה.

שמירת פרטי הכניסה במסד נתונים יחסי כמו MySQL או PostgreSQL בלי מנגנון מטמון הוא עדיין גישה נפוצה ומעשית, אך היא מגיעה עם הגבלות הבאות:

  • תומכת את המסד. היישום חייב לבצע נסיעה חוזרת לשרת בסיס הנתונים כדי לאמת את פרטי הכניסה של המשתמשים מטבלת מסד הנתונים בכל פעם שמשתמש מגיש בקשת התחברות. מאחר והמסד עשוי עדיין לשרת בקשות קריאה/כתיבה אחרות, התהליך כולו מעמיס את מסד הנתונים והופך אותו לאיטי.

  • למסדי נתונים המבוססים על דיסק יש בעיות קינון. כשיישום שלך מקבל אלפי בקשות בשנייה, מסדי הנתונים המבוססים על דיסק אינם פועלים ביעילות מיטבית.

כדי להתמודד עם האתגרים מפורטים לעיל, תוכל להשתמש ב־Redis כדי למטמיע את פרטי הכניסה של המשתמשים כדי שהיישום שלך לא יצטרך ליצור קשר עם מסד הנתונים האחורי בכל בקשת כניסה. Redis הוא אחד מאחסני המידע המהירים ביותר והם משתמשים בזיכרון המרכזי של המחשב שלך כדי לאחסן נתונים בצורת מפתח־ערך. במדריך הזה, תשתמש במסד הנתונים של Redis כדי לשפר את טיפול ההפעלות במושב ביישום הפייתון/MySQL שלך על שרת Ubuntu 22.04.

דרישות מוקדמות

לפני שתתחיל במדריך הזה, יהיה עליך להגדיר את ההגדרות הבאות:

שלב 1 — התקנת מנהלי מסדי נתונים ל־Redis ו־MySQL

היישום הזה מאחסן לצמיתות את פרטי המשתמשים, כגון שמות וסיסמאות, בשרת מסד הנתונים MySQL. כאשר משתמש מתחבר ליישום, סקריפט בפייתון מבצע שאילתה למסד הנתונים של MySQL ומשווה את הפרטים עם הערכים המאוחסנים. לאחר מכן, הסקריפט בפייתון מאחסן במטמון במסד הנתונים של Redis את פרטי ההתחברות של המשתמש לשימוש בבקשות עתידיות. כדי להשלים את הלוגיקה הזו, הסקריפטים שלך בפייתון דורשים מנהגי מסדי הנתונים (מודולים בפייתון) כדי לתקשר עם שרתי MySQL ו-Redis. עקוב אחר השלבים הבאים כדי להתקין את המנהגים הדרושים:

  1. עדכן את אינדקס המידע של החבילות שלך והרץ את הפקודה הבאה כדי להתקין את python3-pip, מנהל החבילות של פייתון שמאפשר לך להתקין מודולים נוספים שאינם חלק מספריית התקנה הסטנדרטית של פייתון.
sudo apt install python3-pip
  1. התקן את מנהג ה-MySQL עבור פייתון:
pip install mysql-connector-python
  1. התקן את מנהג ה-Redis עבור פייתון:
pip install redis

לאחר התקנת המנהגים הדרושים לתקשורת עם MySQL ו-Redis, המשך לשלב הבא ואתחל את מסד הנתונים של MySQL.

שלב 2 — הגדרת מסד נתונים דוגמתי של MySQL

עבור מדריך זה, נדרשת לך טבלה אחת של MySQL. בסביבת ייצור, ניתן להיות לך עשרות טבלאות המכילות בקשות אחרות. הגדר מסד נתונים וצור את הטבלה על ידי ביצוע הפקודות הבאות:

  1. התחבר לשרת מסד הנתונים MySQL כמשתמש root:

    sudo mysql -u root -p
    
  2. הזן את הסיסמה של שרת מסד הנתונים MySQL שלך כשתתבקש ולחץ על ENTER כדי להמשיך. לאחר מכן, הרץ את הפקודה הבאה כדי ליצור מסד נתונים דוגמה בשם company וחשבון משתמש בשם company_user. החלף את example-mysql-password בסיסמה חזקה:

  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. ודא שאתה מקבל את הפלט הבא כדי לאשר שהפקודות הקודמות הורצו בהצלחה:

    פלט
    Query OK, 1 row affected (0.01 sec)
  2. החלף לבסיס הנתונים החדש של החברה:

    1. USE company;
  3. וודא שאתה מחובר לבסיס הנתונים החדש על ידי אימות הפלט הבא:

    הפלט
    Database changed
  4. צור טבלה בשם system_users. עמודת user_id משמשת כמפתח ראשי לזיהוי ייחודי של כל משתמש. עמודות username ו-password הן פרטי התחברות שמשתמשים חייבים להזין כדי להתחבר ליישום. עמודות first_name ו-last_name מאחסנות את שמות המשתמשים:

    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. וודא שיצרת את הטבלה החדשה על ידי בדיקת הפלט הבא:

    פלט
    Query OK, 0 rows affected (0.03 sec)
  6. מלא את טבלת system_users בדוגמאות נתונים. השתמש בפונקציית ה-MD5(...) המובנית של MySQL כדי להצפין את הסיסמה למטרות אבטחה:

    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. אמת את הפלט למטה:

    פלט
    שאילתה OK, 1 שורה מושפעה (0.00 שניות)
  8. שאילתה את טבלת system_users כדי לוודא שהנתונים ממוקמים:

    1. SELECT
    2. user_id,
    3. first_name,
    4. last_name,
    5. password
    6. FROM system_users;
  9. אמת את הפלט הבא:

    פלט
    +---------+------------+-----------+----------------------------------+ | user_id | first_name | last_name | password | +---------+------------+-----------+----------------------------------+ | 1 | JOHN | DOE | 57210b12af5e06ad2e6e54a93b1465aa | | 2 | MARY | HENRY | 259640f97ac2b4379dd540ff4016654c | | 3 | PETER | JADE | 48ef85c894a06a4562268de8e4d934e1 | +---------+------------+-----------+----------------------------------+ 3 שורות ב set (0.00 שניות)
  10. התנתק ממסד הנתונים של MySQL:

    1. QUIT;

עכשיו הגדרת את מסד הנתונים של MySQL הנכון עבור היישום שלך. בשלב הבא, תכין מודול Python שמתקשר עם מסד הנתונים הדוגמה שלך.

שלב 3 — יצירת מודול MySQL Gateway מרכזי עבור Python

בעת כתיבת כל פרויקט Python, עליך ליצור מודול נפרד עבור כל משימה כדי לקדם את ריאות הקוד. בשלב זה, תכין מודול מרכזי שמאפשר לך להתחבר ולשאול את מסד הנתונים של MySQL מתוך סקריפט Python. עקוב אחר השלבים שלמטה:

  1. צור תיקיית project. התיקייה הזו מפרידה את קבצי קוד המקור שלך בפייתון משאר קבצי המערכת:

    1. mkdir project
  2. עבור לתיקיית project החדשה:

    1. cd project
  3. השתמש בעורך הטקסט nano כדי לפתוח קובץ חדש בשם mysql_db.py. קובץ זה מארח את המודול בפייתון שמתקשר עם בסיס הנתונים MySQL:

    nano mysql_db.py
    
  4. הזן את המידע הבא לתוך קובץ mysql_db.py. החלף את example-mysql-password בסיסמת MySQL הנכונה עבור החשבון 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. שמור את הקובץ mysql_db.py וסגור אותו.

בקובץ המודול mysql_db.py ישנה מחלקה אחת (MysqlDb:) עם שני שיטות:
db_con(self):, מתחבר למסד הנתונים company הדוגמה שיצרת בשלב הקודם ומחזיר חיבור MySQL שניתן לשימוש באמצעות ההצהרה return mysql_con.
query(self, username, password):, שיטה שמקבלת username ו-password ומחפשת בטבלת system_users אם יש התאמה. ביטוי התנאי if row_count < 1: ... else: return result[1] מחזיר את הערך הבוליאני False אם המשתמש אינו קיים בטבלה או את הסיסמה של המשתמש (result[1]) אם היישום מוצא התאמה.

עם המודול MySQL מוכן, עקוב אחר השלב הבא כדי להגדיר מודול Redis דומה שמתקשר לאחסון מפתחות Redis.

שלב 4 — יצירת מודול Redis מרכזי עבור Python

בשלב זה, תכתב מודול שמתחבר לשרת Redis. בצע את השלבים הבאים:

  1. פתח קובץ חדש בשם redis_db.py:

    nano redis_db.py
    
  2. הזן את המידע הבא לקובץ redis_db.py. החלף example-redis-password בסיסמה הנכונה עבור שרת ה-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. שמור וסגור את קובץ redis_db.py.

  • בקובץ לעיל יש תיקייה אחת (RedisDb:).

  • תחת התיקייה הזו, השיטה db_con(self): משתמשת בפרטי הכניסה המסופקים כדי להתחבר לשרת Redis ולהחזיר חיבור המשתמש באמצעות ההצהרה return redis_con.

לאחר שהגדרת את מחלקת Redis, צור את קובץ המרכז של הפרויקט בשלב הבא.

שלב 5 — יצירת נקודת הכניסה של היישום

לכל היישום בפייתון חייבת להיות נקודת כניסה או קובץ הראשי שמופעל כאשר היישום מתבצע. בקובץ זה, תיצור קוד שמציג את הזמן הנוכחי של השרת עבור משתמשים מאומתים. קובץ זה משתמש במודולים המותאמים אישית של MySQL ו-Redis שיצרת כדי לאמת משתמשים. עקוב אחר השלבים הבאים כדי ליצור את הקובץ:

  1. פתח קובץ חדש בשם index.py:

    nano index.py
    
  2. הזן את המידע הבא לקובץ 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": "שם משתמש או סיסמה לא תקינים."}
                else:
                    resp = {"time": current_time, "authorized by": "שרת Redis"}
            else:
                mysql_resp  = mysql_server.query(auth_user, auth_password)
                if mysql_resp == False:
                     resp =  {"error": "שם משתמש או סיסמה לא תקינים."}
                else:
                    resp = {"time": current_time, "authorized by": "שרת 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("שרת האינטרנט פועל בפורט 8080...")
    
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        httpd.server_close()
        print("שרת האינטרנט נעצר.")
    
  3. שמור וסגור את קובץ index.py

  • בקובץ index.py, בסעיף הייבוא (import...) הוסף את המודולים הבאים לפרויקט שלך:

    • utf_8, base64, md5, ו־json – מודולים לקידוד ועיצוב טקסט.

    • http.server, HTTPStatus, ו־socketserver – מודולים לשרת רשת.

    • datetime – מודול לטיפול בזמן ותאריך.

    • mysql_db ו־redis_db – מודולים מותאמים אישית שנוצרו מראש כדי לגשת לשרתי MySQL ו־Redis.

  • המחלקה HttpHandler(http.server.SimpleHTTPRequestHandler): היא מחלקת מטפלת עבור שרת ה-HTTP. תחת המחלקה, השיטה do_GET(self): מטפלת בבקשות ה-HTTP מסוג GET ומציגה את תאריך/שעת המערכת עבור משתמשים מאומתים.

  • בלוגיקת ה־if ... : else: ..., התסריט בפייתון מפעיל את ההצהרה הלוגית if redis_client.exists(auth_user): כדי לבדוק האם פרטי המשתמש קיימים בשרת ה־Redis. אם פרטי המשתמש קיימים והסיסמה שמורה ב־Redis אינה תואמת את הסיסמה שהמשתמש הזין, היישום מחזיר את השגיאה {"error": "שם משתמש/סיסמה לא חוקיים."}.

אם פרטי המשתמש אינם קיימים בשרת Redis, היישום מבצע שאילתה לשרת מסד הנתונים MySQL באמצעות ההצהרה mysql_resp = mysql_server.query(auth_user, auth_password). במקרה שבו הסיסמה שסופקה על ידי המשתמש אינה תואמת את הערך המאוחסן במסד הנתונים, היישום מחזיר את השגיאה {"error": "שם משתמש/סיסמה לא תקינים."}. אחרת, היישום מאחסן את פרטי המשתמש בשרת Redis באמצעות ההצהרה redis_client.set(auth_user, mysql_resp).

  • בכל המקרים בהם הפרטים של המשתמש תואמים לפרטי Redis/MySQL, היישום מציג את תאריך/שעת המערכת הנוכחית באמצעות ההצהרה {"time": current_time, ...}. הערך authorized by בפלט מאפשר לך לראות את שרת המסד נתונים שמאמת את המשתמשים ביישום.

      if redis_client.exists(auth_user):
          if md5(auth_password.encode('utf8')).hexdigest() != redis_client.get(auth_user).decode('utf8'):
              resp = {"error": "שם משתמש/סיסמה לא חוקיים."}
          else:
              resp = {"time": current_time, "authorized by": "שרת Redis"}
      else:
          mysql_resp  = mysql_server.query(auth_user, auth_password)
          if mysql_resp == False:
               resp =  {"error": "שם משתמש/סיסמה לא חוקיים."}
          else:
              resp = {"time": current_time, "authorized by": "שרת MySQL"}
              redis_client.set(auth_user, mysql_resp)   
    

כעת הגדרת את הקובץ הראשי לאפליקציה. בשלב הבא תבדוק את האפליקציה.

שלב 6 — בדיקת האפליקציה

בשלב זה, תפעיל את האפליקציה כדי לבדוק אם המנגנון המטמון של Redis פועל. בצע את הפקודות הבאות כדי לבדוק את האפליקציה:

  1. השתמש בפקודה python3 הבאה כדי להפעיל את האפליקציה:

    python3 index.py
    
  2. ודא ששרת האינטרנט המותאם אישית של האפליקציה פועל:

    פלט
    שרת האינטרנט פועל בפורט 8080...
  3. צור חיבור SSH נוסף לשרת שלך בחלון טרמינל חדש והרץ את הפקודות curl הבאות כדי לשלוח ארבעה בקשות GET באמצעות הפרטים המזהים של john_doe. הוסף [1-4] בסוף כתובת ה-URL http://localhost:8080/ כדי לשלוח את הבקשות הארבע בפקודה יחידה:

    curl -X GET -u john_doe:password_1  http://localhost:8080/[1-4]
    
  4. אמת את הפלטים הבאים. השרת של MySQL מכין את הבקשה הראשונה לאימות. לאחר מכן, מסד הנתונים של Redis מכין את הבקשות הבאות שלוש.

    פלט
    [1/4] { "time": "2023-11-07 10:04:38", "authorized by": "שרת MySQL" } [4/4] { "time": "2023-11-07 10:04:38", "authorized by": "שרת Redis" }

הלוגיקה של היישום שלך עובדת כפי שצפוי.

מסקנה

במדריך זה, בנית התוכנה שלך בשפת פייתון שמשתמשת בשרת ה-Redis כדי לאחסן באופן מטמון את פרטי הכניסה של המשתמשים. Redis הוא שרת מסד נתונים זמין וקלט גודל שיכול לבצע אלפי עסקאות בשניה. בעזרת מנגנון המטמון של Redis ביישום שלך, אתה יכול להפחית מאוד את התעבורה בשרת מסד הנתונים האחורי שלך. כדי ללמוד עוד על יישומי Redis, יש לעיין במדריכים שלנו ב-Redis.

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