كيفية تسريع معالجة جلسات التطبيق Python/MySQL باستخدام Redis على Ubuntu 22.04

مقدمة

المصادقة هي عملية التحقق من هوية المستخدمين أثناء طلبات تسجيل الدخول. في عملية المصادقة ، يقدم المستخدمون بيانات اعتمادهم كأسماء مستخدمين وكلمات مرور. ثم يقوم التطبيق بمطابقة تلك بيانات الاعتماد مع إدخالات قاعدة البيانات المخزنة. يمنح التطبيق المستخدمين الوصول إلى النظام في حالة وجود تطابق.

تخزين بيانات اعتماد تسجيل الدخول في قاعدة بيانات علاقية مثل MySQL أو PostgreSQL بدون آلية تخزين مؤقت لا يزال يعتبر نهجًا شائعًا وعمليًا ، ولكنه يأتي مع القيود التالية:

  • تحميل القاعدة بشكل زائد. يجب أن يقوم التطبيق بالذهاب والعودة إلى خادم قاعدة البيانات للتحقق من بيانات اعتماد المستخدمين من جدول قاعدة البيانات في كل مرة يقدم فيها المستخدم طلب تسجيل دخول. نظرًا لأن قاعدة البيانات قد تخدم لا يزال طلبات قراءة / كتابة أخرى ، يتسبب العملية بأكملها في تحميل القاعدة بشكل زائد وتجعلها بطيئة.

  • قواعد البيانات التقليدية المستندة إلى القرص لديها مشاكل في التوسعية. عندما يتلقى تطبيقك آلاف الطلبات في الثانية ، لا تعمل قواعد البيانات المستندة إلى القرص بشكل مثالي.

للتغلب على التحديات المذكورة أعلاه، يمكنك استخدام Redis لتخزين ذاكرة التخزين المؤقت لبيانات تسجيل الدخول للمستخدمين بحيث لا يحتاج التطبيق الخاص بك للاتصال بقاعدة البيانات الخلفية أثناء كل طلب تسجيل دخول. Redis هو أحد أشهر مخازن البيانات فائقة السرعة التي تستخدم ذاكرة الوصول العشوائي للكمبيوتر الخاص بك لتخزين البيانات في أزواج المفاتيح والقيم. في هذا الدليل، ستستخدم قاعدة بيانات Redis لتسريع معالجة الجلسات في تطبيق Python/MySQL الخاص بك على خادم Ubuntu 22.04.

المتطلبات الأساسية

قبل أن تبدأ في هذا البرنامج التعليمي، ستحتاج إلى إعداد ما يلي:

الخطوة 1 – تثبيت برامج تشغيل قواعد بيانات Python لـ Redis و MySQL

تقوم هذه التطبيقات بتخزين بيانات اعتماد المستخدمين بشكل دائم، مثل الأسماء وكلمات المرور، في خادم قاعدة بيانات MySQL. عندما يقوم المستخدم بتسجيل الدخول إلى التطبيق، يقوم نص برمجي بلغة Python بالاستعلام عن قاعدة بيانات MySQL ومطابقة التفاصيل مع القيم المخزنة. بعد ذلك، يقوم نص برمجي بلغة Python بتخزين بيانات تسجيل دخول المستخدم في قاعدة بيانات Redis لخدمة طلبات أخرى في المستقبل. لإكمال هذه الخطط، يحتاج نصوص برمجية بلغة Python إلى برامج تشغيل قواعد البيانات (وحدات Python) للتواصل مع خوادم MySQL و Redis. اتبع الخطوات التالية لتثبيت برامج التشغيل:

  1. قم بتحديث مؤشر معلومات الحزم وقم بتشغيل الأمر التالي لتثبيت python3-pip، وهو مدير حزم Python يسمح لك بتثبيت وحدات إضافية ليست جزءًا من مكتبة Python القياسية.
sudo apt install python3-pip
  1. قم بتثبيت برنامج تشغيل MySQL for Python:
pip install mysql-connector-python
  1. قم بتثبيت برنامج تشغيل Redis for Python:
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. التبديل إلى قاعدة البيانات الجديدة company:

    1. USE company;
  3. تأكيد الاتصال بقاعدة البيانات الجديدة من خلال التحقق من الناتج التالي:

    تم تغيير قاعدة البيانات
  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. تحقق من النتيجة أدناه:

    النتيجة
    تم تنفيذ الاستعلام بنجاح، تم تأثير صف واحد (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 صفوف في المجموعة (0.00 ثانية)
  10. تسجيل الخروج من قاعدة بيانات MySQL:

    1. QUIT;

لقد قمت الآن بإعداد قاعدة بيانات MySQL الصحيحة لتطبيقك. في الخطوة التالية، ستقوم بإنشاء وحدة Python تتواصل مع قاعدة البيانات الخاصة بك.

الخطوة 3 – إنشاء وحدة بوابة MySQL المركزية لـ Python

عند كتابة أي مشروع Python، يجب عليك إنشاء وحدة منفصلة لكل مهمة لتعزيز إعادة استخدام الكود. في هذه الخطوة، ستقوم بإعداد وحدة مركزية تسمح لك بالاتصال واستعلام قاعدة بيانات MySQL من خلال نص Python. اتبع الخطوات التالية:

  1. أنشئ مجلد project. يفصل هذا المجلد ملفات الشفرة الخاصة بك من باقي ملفات النظام:

    1. mkdir project
  2. انتقل إلى مجلد المشروع الجديد:

    1. cd project
  3. استخدم محرر النص nano لفتح ملف mysql_db.py الجديد. يحتوي هذا الملف على الوحدة النمطية في Python التي تتحدث إلى قاعدة بيانات 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 – إنشاء نقطة الدخول للتطبيق

يجب أن يحتوي كل تطبيق Python على نقطة دخول أو الملف الرئيسي الذي يتم تنفيذه عند تشغيل التطبيق. في هذا الملف، ستقوم بإنشاء رمز يعرض وقت الخادم الحالي للمستخدمين المصادق عليهم. يستخدم هذا الملف وحدات 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, "تم التحقق من الشخصية بواسطة": "خادم Redis"}
            else:
                mysql_resp  = mysql_server.query(auth_user, auth_password)
                if mysql_resp == False:
                     resp =  {"error": "اسم المستخدم/كلمة المرور غير صحيحة."}
                else:
                    resp = {"time": current_time, "تم التحقق من الشخصية بواسطة": "خادم 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" }

تعمل منطق التطبيق الخاص بك الآن كما هو متوقع.

الاستنتاج

في هذا الدليل، قمت ببناء تطبيق برمجي بلغة Python يستخدم خادم 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