مقدمة
Flask هو إطار عمل ويب خفيف الوزن ومرن لبناء تطبيقات صغيرة ومتوسطة الحجم. يُستخدم عادةً في مشاريع تتراوح من المدونات الشخصية البسيطة إلى التطبيقات الأكثر تعقيدًا، مثل واجهات برمجة تطبيقات REST، ومنصات SaaS، ومواقع التجارة الإلكترونية، ولوحات المعلومات المُدارة بالبيانات.
مع ذلك، مع ازدياد عدد الزيارات أو التعقيد في تطبيقك، قد تلاحظ اختناقات في الأداء. سواء كنت تُنشئ نظام إدارة محتوى (CMS)، أو واجهة برمجة تطبيقات لتطبيق جوال، أو أداة لتصور البيانات في الوقت الفعلي، فإن تحسين أداء Flask أمر بالغ الأهمية لتوفير تجربة مستخدم سريعة الاستجابة وقابلة للتطوير.
في هذا البرنامج التعليمي، سوف تستكشف التقنيات وأفضل الممارسات لتحسين أداء تطبيق Flask.
المتطلبات الأساسية
- خادم يعمل بنظام أوبونتو، ومستخدم غير جذر بصلاحيات sudo، وجدار حماية مُفعّل. للحصول على إرشادات حول كيفية إعداد هذا، يُرجى اختيار توزيعتك من هذه القائمة واتباع دليلنا "بدء استخدام الخادم". يُرجى التأكد من استخدام إصدار مدعوم من أوبونتو.
- التعرف على سطر أوامر Linux للحصول على مقدمة أو تحديث للمعلومات حول سطر الأوامر، يمكنك مراجعة هذا الدليل حول Linux Command Line Primer.
- فهم أساسي لبرمجة بايثون.
- يجب تثبيت إصدار بايثون 3.7 أو أحدث على نظام أوبونتو. لمعرفة كيفية تشغيل سكربتات بايثون على أوبونتو، يمكنك مراجعة دليلنا التعليمي حول كيفية تشغيل سكربتات بايثون على أوبونتو.
إعداد بيئة Flask الخاصة بك
يأتي أوبونتو 24.04 مزودًا بـ Python 3 افتراضيًا. افتح نافذة طرفية وشغّل الأمر التالي للتحقق من تثبيت Python 3:
root@ubuntu:~# python3 --version
Python 3.12.3إذا كان بايثون 3 مُثبّتًا على جهازك، فسيُعيد الأمر أعلاه الإصدار الحالي من تثبيت بايثون 3. إذا لم يكن مُثبّتًا، يمكنك تشغيل الأمر التالي للحصول على تثبيت بايثون 3:
root@ubuntu:~# sudo apt install python3
بعد ذلك، ستحتاج إلى تثبيت برنامج التثبيت الخاص بحزمة pip على نظامك:
root@ubuntu:~# sudo apt install python3-pip
بعد تثبيت pip، دعنا نقوم بتثبيت Flask.
سيتم تثبيت Flask عبر pip. يُنصح بإجراء ذلك في بيئة افتراضية لتجنب التداخل مع الحزم الأخرى على نظامك.
root@ubuntu:~# python3 -m venv myprojectenv root@ubuntu:~# source myprojectenv/bin/activate
root@ubuntu:~# pip install Flask
إنشاء تطبيق Flask
الخطوة التالية هي كتابة شيفرة بايثون لتطبيق Flask. لإنشاء نص برمجي جديد، انتقل إلى المجلد الذي تختاره:
root@ubuntu:~# cd ~/path-to-your-script-directory
بعد دخولك إلى المجلد، أنشئ ملف بايثون جديد، app.py، واستورد Flask. بعد ذلك، شغّل تطبيق Flask وأنشئ مسارًا جذريًا.
root@ubuntu:~# nano app.py
سيؤدي هذا إلى فتح محرر نصوص فارغ. اكتب منطقك هنا أو انسخ الكود أدناه:
from flask import Flask, jsonify, request
app = Flask(__name__)
# Simulate a slow endpoint
@app.route('/slow')
def slow():
import time
time.sleep(2) # to simulate a slow response
return jsonify(message="This request was slow!")
# Simulate an intensive database operation
@app.route('/db')
def db_operation():
# This is a dummy function to simulate a database query
result = {"name": "User", "email": "[email protected]"}
return jsonify(result)
# Simulate a static file being served
@app.route('/')
def index():
return "<h1>Welcome to the Sample Flask App</h1>"
if __name__ == '__main__':
app.run(debug=True)الآن دعونا نقوم بتشغيل تطبيق Flask:
root@ubuntu:~# flask run
يمكنك اختبار نقاط النهاية باستخدام أوامر curl التالية:
اختبار نقطة النهاية / (إرجاع محتوى ثابت):
root@ubuntu:~# curl http://127.0.0.1:5000/[secondary_lebel Output]
<h1>Welcome to the Sample Flask App</h1>%اختبار نقطة النهاية/البطء (محاكاة الاستجابة البطيئة):
root@ubuntu:~# time curl http://127.0.0.1:5000/db
للتحقق من هذه النقطة البطيئة، نستخدم أمر الوقت في لينكس. يُستخدم هذا الأمر لقياس وقت تنفيذ أمر أو برنامج معين. يوفر هذا الأمر ثلاث معلومات رئيسية:
- الوقت الحقيقي: الوقت الفعلي المنقضي من بداية الأمر إلى نهايته.
- وقت المستخدم: مقدار الوقت الذي يقضيه وحدة المعالجة المركزية في وضع المستخدم.
- وقت النظام: مقدار الوقت الذي يقضيه وحدة المعالجة المركزية في وضع kernel.
سيساعدنا هذا في قياس الوقت الفعلي الذي تستغرقه نقطة النهاية البطيئة لدينا. قد يبدو الناتج كالتالي:
Output
{"message":"This request was slow!"}
curl http://127.0.0.1:5000/slow 0.00s user 0.01s system 0% cpu 2.023 totalيستغرق هذا الطلب حوالي ثانيتين للاستجابة، لأن استدعاء time.sleep(2) يحاكي استجابة بطيئة.
دعنا نختبر نقطة نهاية /db (تحاكي عمليات قاعدة البيانات):
root@ubuntu:~# curl http://127.0.0.1:5000/db
Output
{"email":"[email protected]","name":"User"}من خلال اختبار نقاط النهاية هذه باستخدام curl، يمكنك التأكد من تشغيل تطبيق Flask بشكل صحيح ومن أن الاستجابات كما هو متوقع.
في القسم التالي، سوف تتعلم كيفية تحسين أداء التطبيق باستخدام تقنيات مختلفة.
استخدم خادم WSGI جاهزًا للإنتاج
خادم تطوير Flask المدمج غير مُصمم لبيئات الإنتاج. للتعامل بفعالية مع الطلبات المتزامنة، يُنصح بالانتقال إلى خادم WSGI جاهز للإنتاج مثل Gunicorn.
تثبيت وتشغيل Gunicorn
دعونا نقوم بتثبيت Gunicorn.
root@ubuntu:~# pip install gunicornقم بتشغيل تطبيق Flask باستخدام Gunicorn مع 4 عمليات عاملة:
root@ubuntu:~# gunicorn -w 4 -b 0.0.0.0:8000 app:app
Output
% /Library/Python/3.9/bin/gunicorn -w 4 -b 0.0.0.0:8000 app:app
[2024-09-13 18:37:24 +0530] [99925] [INFO] Starting gunicorn 23.0.0
[2024-09-13 18:37:24 +0530] [99925] [INFO] Listening at: http://0.0.0.0:8000 (99925)
[2024-09-13 18:37:24 +0530] [99925] [INFO] Using worker: sync
[2024-09-13 18:37:24 +0530] [99926] [INFO] Booting worker with pid: 99926
[2024-09-13 18:37:25 +0530] [99927] [INFO] Booting worker with pid: 99927
[2024-09-13 18:37:25 +0530] [99928] [INFO] Booting worker with pid: 99928
[2024-09-13 18:37:25 +0530] [99929] [INFO] Booting worker with pid: 99929
[2024-09-13 18:37:37 +0530] [99925] [INFO] Handling signal: winch
^C[2024-09-13 18:38:51 +0530] [99925] [INFO] Handling signal: int
[2024-09-13 18:38:51 +0530] [99927] [INFO] Worker exiting (pid: 99927)
[2024-09-13 18:38:51 +0530] [99926] [INFO] Worker exiting (pid: 99926)
[2024-09-13 18:38:51 +0530] [99928] [INFO] Worker exiting (pid: 99928)
[2024-09-13 18:38:51 +0530] [99929] [INFO] Worker exiting (pid: 99929)
[2024-09-13 18:38:51 +0530] [99925] [INFO] Shutting down: Masterفيما يلي فوائد استخدام Gunicorn:
- معالجة الطلبات المتزامنة: يسمح Gunicorn بمعالجة طلبات متعددة في وقت واحد باستخدام عمليات عاملة متعددة.
- موازنة التحميل: موازنة الطلبات الواردة عبر عمليات العمال وتضمن الاستخدام الأمثل لموارد الخادم.
- العاملون غير المتزامنون: باستخدام العاملين غير المتزامنين مثل gevent، يمكنه تنفيذ المهام الطويلة الأمد بكفاءة دون حظر الطلبات الأخرى.
- إمكانية التوسع: يمكن لـ Gunicorn التوسع أفقيًا عن طريق زيادة عدد عمليات العمال للتعامل مع المزيد من الطلبات المتزامنة.
- التسامح مع الأخطاء: استبدال العمال الذين لا يستجيبون أو الذين يعانون من الأعطال تلقائيًا، مما يضمن توفرًا عاليًا.
- جاهز للإنتاج: على عكس خادم تطوير Flask، تم تحسين Gunicorn لبيئات الإنتاج مع ميزات أمان واستقرار وأداء أفضل.
من خلال التبديل إلى Gunicorn للإنتاج، يمكنك تحسين معدل الإنتاج والاستجابة لتطبيق Flask الخاص بك بشكل كبير، وإعداده للتعامل بكفاءة مع حركة المرور في العالم الحقيقي.
تمكين التخزين المؤقت لتقليل الحمل.
يُعد التخزين المؤقت من أفضل الطرق لتحسين أداء Flask بتقليل التكلفة. هنا، يمكنك إضافة Flask-Caching لتخزين نتائج المسار البطيء مؤقتًا.
تثبيت وتكوين Flask-Caching مع Redis
تثبيت الحزم اللازمة:
تحديث app.py لإضافة ذاكرة التخزين المؤقت إلى slow/path
افتح المحرر وقم بتحديث ملف app.py باستخدام الأمر التالي:
root@ubuntu:~# nano app.py
from flask_caching import Cache
app = Flask(__name__)
# Configure Flask-Caching with Redis
app.config['CACHE_TYPE'] = 'redis'
app.config['CACHE_REDIS_HOST'] = 'localhost'
app.config['CACHE_REDIS_PORT'] = 6379
cache = Cache(app)
@app.route('/slow')
@cache.cached(timeout=60)
def slow():
import time
time.sleep(2) # Simulate a slow response
return jsonify(message="This request was slow!")بعد الطلب الأول على /slow، تُنفَّذ الطلبات اللاحقة من ذاكرة التخزين المؤقت خلال 60 ثانية، متجاوزةً دالة time.sleep(). هذا يُخفِّف حمل الخادم ويزيد من سرعة الاستجابة.
ملاحظة: في هذا البرنامج التعليمي، نستخدم localhost كمضيف Redis. مع ذلك، في بيئة الإنتاج، يُنصح باستخدام خدمة Redis مُدارة مثل DigitalOcean Managed Redis. يوفر هذا قابلية توسع وموثوقية وأمانًا أفضل لاحتياجات التخزين لديك. يمكنك معرفة المزيد حول دمج DigitalOcean Managed Redis في تطبيق إنتاجي في هذا البرنامج التعليمي حول التخزين باستخدام DigitalOcean Redis على منصة التطبيقات.
للتحقق مما إذا كان يتم تخزين البيانات مؤقتًا، نقوم بتشغيل الأوامر التالية لنقطة النهاية /slow.
هذا هو الطلب الأول لنقطة النهاية /slow. بعد اكتمال هذا الطلب، تُخزَّن نتيجة مسار /slow مؤقتًا.
root@ubuntu:~# time curl http://127.0.0.1:5000/slow
Output
{"message":"This request was slow!"}
curl http://127.0.0.1:5000/slow 0.00s user 0.01s system 0% cpu 2.023 totalهذا طلب لاحق لنقطة النهاية /slow خلال 60 ثانية:
root@ubuntu:~# time curl http://127.0.0.1:5000/slow
Output
{"message":"This request was slow!"}
curl http://127.0.0.1:5000/slow 0.00s user 0.00s system 0% cpu 0.015 totalتحسين استعلامات قاعدة البيانات
غالبًا ما تُشكّل استعلامات قواعد البيانات عائقًا في الأداء. في هذا القسم، ستُحاكي تحسين استعلامات قواعد البيانات باستخدام SQLAlchemy وتجميع الوصلات.
محاكاة استعلام قاعدة البيانات باستخدام التجميع المشترك
أولاً، دعنا نقوم بتثبيت SQLAlchemy.
root@ubuntu:~# pip install Flask-SQLAlchemy
قم بتحديث app.py لتكوين تكامل الاتصال.
rom flask_sqlalchemy import SQLAlchemy
from sqlalchemy import text
# Simulate an intensive database operation
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_POOL_SIZE'] = 5 # Connection pool size
db = SQLAlchemy(app)
@app.route('/db1')
def db_operation_pooling():
# Simulate a database query
result = db.session.execute(text('SELECT 1')).fetchall()
return jsonify(result=str(result))الآن، عندما ننفذ طلب curl إلى مسار db1، يجب أن نرى الإخراج التالي:
root@ubuntu:~# curl http://127.0.0.1:5000/db1
output
{"result":"[(1,)]"}يمكنك تحسين أداء تطبيق Flask بشكل ملحوظ من خلال تطبيق تجميع الاتصالات في بيئة إنتاجية. يتيح تجميع الاتصالات للتطبيق إعادة استخدام اتصالات قاعدة البيانات الحالية بدلاً من إنشاء اتصالات جديدة لكل طلب. هذا يُقلل من تكلفة إنشاء اتصالات جديدة، مما يُؤدي إلى أوقات استجابة أسرع وقابلية توسع مُحسّنة.
يُحدد تكوين SQLALCHEMY_POOL_SIZE الذي حددناه سابقًا عدد الاتصالات في المجموعة. يجب تعديل هذه القيمة في بيئة الإنتاج وفقًا لاحتياجاتك وقدرات الخادم. بالإضافة إلى ذلك، قد ترغب في النظر في خيارات تكامل أخرى، مثل SQLALCHEMY_MAX_OVERFLOW للسماح باتصالات إضافية في حال امتلاء المجموعة، وSQLALCHEMY_POOL_TIMEOUT لتحديد مدة انتظار الطلب للاتصال.
تذكر، بينما يستخدم مثالنا SQLite لتبسيط الأمور، فمن المرجح أنك في الواقع تستخدم قاعدة بيانات أكثر متانة مثل PostgreSQL أو MySQL. تتميز قواعد البيانات هذه بآليات تجميع اتصالات خاصة بها، والتي يمكن استخدامها مع تكامل SQLAlchemy لتحسين الأداء.
من خلال التكوين الدقيق واستخدام تجمع الاتصالات، يمكنك التأكد من أن تطبيق Flask الخاص بك يقوم بعمليات قاعدة البيانات بكفاءة حتى تحت الحمل الثقيل، وبالتالي تحسين أدائه العام بشكل كبير.
تمكين ضغط Gzip
قد يؤدي ضغط استجاباتك إلى تقليل كمية البيانات المنقولة بين الخادم والعملاء بشكل كبير وتحسين الأداء.
تثبيت وتكوين Flask-Compress
لنبدأ بتثبيت حزمة Flask-compress.
root@ubuntu:~# pip install Flask-Compress
بعد ذلك، دعنا نقوم بتحديث app.py لتمكين الضغط.
from flask_compress import Compress
# This below command enables Gzip compression for the Flask app
# It compresses responses before sending them to clients,
# reducing data transfer and improving performance
Compress(app)
@app.route('/compress')
def Compress():
return "<h1>Welcome to the optimized Flask app !</h1>"يؤدي هذا تلقائيًا إلى ضغط الاستجابات التي يزيد حجمها عن 500 بايت، مما يقلل من وقت نقل الاستجابات الكبيرة.
في بيئة الإنتاج، يمكن لضغط Gzip أن يقلل بشكل كبير من كمية البيانات المنقولة بين الخادم والعملاء، وخاصةً للمحتوى النصي مثل HTML وCSS وJavaScript.
يؤدي هذا التخفيض في نقل البيانات إلى تسريع تحميل الصفحات، وتحسين تجربة المستخدم، وخفض تكاليف النطاق الترددي. بالإضافة إلى ذلك، تدعم العديد من متصفحات الويب الحديثة ضغط Gzip تلقائيًا، مما يجعله تقنية تحسين متوافقة تمامًا. بتفعيل ضغط Gzip، يمكنك تحسين أداء تطبيق Flask وقابليته للتوسع بفعالية دون الحاجة إلى إجراء أي تغييرات على جانب العميل.
تحميل المهام المكثفة إلى Celery
للعمليات كثيفة الموارد، مثل إرسال رسائل البريد الإلكتروني أو معالجة مجموعات البيانات الضخمة، يُفضّل نقلها إلى مهام خلفية باستخدام Celery. هذا يمنع المهام طويلة الأمد من حظر الطلبات الواردة.
Celery هو نظام قوي لإدارة طوابير المهام الموزعة، يتيح لك تنفيذ المهام التي تستغرق وقتًا طويلاً بشكل غير متزامن. بتفويض العمليات المكثفة إلى Celery، يمكنك تحسين استجابة تطبيق Flask وقابليته للتوسع بشكل ملحوظ. يعمل Celery عن طريق تفويض المهام إلى عمليات عاملة تعمل على أجهزة منفصلة، مما يسمح بتحسين استخدام الموارد والمعالجة المتوازية.
تتضمن الفوائد الرئيسية لاستخدام الكرفس ما يلي:
- تحسين وقت الاستجابة لطلبات المستخدم
- تحسين قابلية التوسع وإدارة الموارد
- القدرة على أداء المهام المعقدة والتي تستغرق وقتًا طويلاً دون حظر البرنامج الرئيسي
- دعم مدمج لجدولة المهام وإعادة محاولة تنفيذ المهام الفاشلة
- التكامل السهل مع مختلف وسطاء الرسائل مثل RabbitMQ أو Redis
باستخدام Celery، يمكنك التأكد من أن تطبيق Flask الخاص بك يظل مستجيباً حتى عند التعامل مع المهام التي تتطلب قدراً كبيراً من الحوسبة أو المهام المرتبطة بالإدخال/الإخراج.
تكوين Celery للمهام الخلفية
دعونا نقوم بتثبيت الكرفس.
root@ubuntu:~# pip install Celery
بعد ذلك، دعنا نقوم بتحديث app.py لتكوين Celery للمهام غير المتزامنة:
from celery import Celery
celery = Celery(app.name, broker='redis://localhost:6379/0')
@celery.task
def long_task():
import time
time.sleep(10) # Simulate a long task
return "Task Complete"
@app.route('/start-task')
def start_task():
long_task.delay()
return 'Task started'في محطة منفصلة، قم بتشغيل عامل Celery:
root@ubuntu:~# celery -A app.celery worker --loglevel=info
Output
------------- celery@your-computer-name v5.2.7 (dawn-chorus)
--- ***** -----
-- ******* ---- Linux-x.x.x-x-generic-x86_64-with-glibc2.xx 2023-xx-xx
- *** --- * ---
- ** ---------- [config]
- ** ---------- .> app: app:0x7f8b8c0b3cd0
- ** ---------- .> transport: redis://localhost:6379/0
- ** ---------- .> results: disabled://
- *** --- * --- .> concurrency: 8 (prefork)
-- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)
--- ***** -----
-------------- [queues]
.> celery exchange=celery(direct) key=celery
[tasks]
. app.long_task
[2023-xx-xx xx:xx:xx,xxx: INFO/MainProcess] Connected to redis://localhost:6379/0
[2023-xx-xx xx:xx:xx,xxx: INFO/MainProcess] mingle: searching for neighbors
[2023-xx-xx xx:xx:xx,xxx: INFO/MainProcess] mingle: all alone
[2023-xx-xx xx:xx:xx,xxx: INFO/MainProcess] celery@your-computer-name ready.الآن قم بتشغيل أمر curl للوصول إلى مسار /start-task، وسيكون الناتج كما يلي:
root@ubuntu:~# curl http://127.0.0.1:5000/start-task
Output
Task startedسيؤدي هذا إلى إرجاع "بدأت المهمة" على الفور تقريبًا، حتى لو كانت المهمة الخلفية لا تزال قيد التشغيل.
تؤدي وظيفة start_task() شيئين:
- يستدعي long_task.delay()، الذي يبدأ مهمة Celery بشكل غير متزامن. هذا يعني أن المهمة مُدرجة في قائمة انتظار التشغيل في الخلفية، لكن الدالة لا تنتظر اكتمالها.
- يقوم على الفور بإرجاع السلسلة "بدأت المهمة".
من المهم ملاحظة أن المهمة الفعلية طويلة الأمد (التي تُحاكيها فترة السكون التي تبلغ 10 ثوانٍ) تُنفَّذ بشكل غير متزامن بواسطة Celery. لا ينتظر مسار Flask اكتمال هذه المهمة قبل الاستجابة للطلب.
لذا، عندما تقوم بتعطيل هذه النقطة النهائية، ستتلقى على الفور استجابة مفادها "بدأت المهمة"، بينما تستمر المهمة الفعلية في العمل في الخلفية لمدة 10 ثوانٍ.
بعد مرور 10 ثوانٍ عند اكتمال المهمة الخلفية، يجب أن ترى رسالة التقرير هذه:
سيكون الناتج مشابهًا لهذا:
[2024-xx-xx xx:xx:xx,xxx: INFO/MainProcess] Task app.long_task[task-id] received
[2024-xx-xx xx:xx:xx,xxx: INFO/ForkPoolWorker-1] Task app.long_task[task-id] succeeded in 10.xxxs: 'Task Complete'يوضح هذا المثال كيف يُحسّن Celery أداء تطبيق Flask من خلال تنفيذ المهام طويلة الأمد بشكل غير متزامن، مع الحفاظ على استجابة التطبيق الرئيسي. تعمل هذه المهام في الخلفية، مما يُتيح لتطبيق Flask التعامل مع طلبات أخرى.
في بيئة الإنتاج، يتضمن تشغيل Celery ما يلي:
- استخدام وسيط رسائل قوي مثل RabbitMQ
- استخدام واجهة نتائج خاصة (على سبيل المثال، PostgreSQL)
- إدارة العمال باستخدام أنظمة التحكم في العمليات (على سبيل المثال، المشرف)
- تنفيذ أدوات المراقبة (على سبيل المثال، الزهور) وزيادة معالجة الأخطاء وتسجيلها
- تحسين معالجة الأخطاء وتسجيلها
- استخدم أولوية المهام
- التوسع مع العديد من العمال على أجهزة مختلفة
- ضمان التدابير الأمنية المناسبة
نتيجة
في هذا البرنامج التعليمي، تعلمت كيفية تحسين تطبيق Flask الخاص بك من خلال تطبيق تقنيات متنوعة لتحسين الأداء. باتباع هذه الخطوات، يمكنك تحسين أداء تطبيق Flask وقابليته للتوسع واستجابته، مما يضمن عمله بكفاءة حتى في ظل الأحمال الثقيلة.









