مقدمه
Flask یک چارچوب وب سبک و انعطاف پذیر برای ساخت برنامه های کاربردی کوچک تا متوسط است. معمولاً در پروژههایی از وبلاگهای شخصی ساده گرفته تا برنامههای پیچیدهتر، مانند REST API، پلتفرمهای SaaS، وبسایتهای تجارت الکترونیک و داشبوردهای مبتنی بر داده استفاده میشود.
با این حال، با افزایش حجم برنامه شما در ترافیک یا افزایش پیچیدگی، ممکن است متوجه گلوگاه های عملکرد شوید. چه در حال ساخت یک سیستم مدیریت محتوا (CMS)، یک API برای یک برنامه تلفن همراه یا یک ابزار تجسم داده در زمان واقعی باشید، بهینه سازی عملکرد Flask برای ارائه یک تجربه کاربری پاسخگو و مقیاس پذیر بسیار مهم است.
در این آموزش، تکنیکها و بهترین روشها را برای بهینهسازی عملکرد یک اپلیکیشن Flask بررسی خواهید کرد.
پیش نیازها
- سروری که اوبونتو را اجرا می کند و کاربر غیر ریشه ای با امتیازات sudo و فایروال فعال. برای راهنمایی در مورد نحوه تنظیم این، لطفاً توزیع خود را از این لیست انتخاب کنید و راهنمای اولیه راه اندازی سرور ما را دنبال کنید. لطفاً مطمئن شوید که با نسخه پشتیبانی شده اوبونتو کار می کنید.
- آشنایی با خط فرمان لینوکس برای معرفی یا تجدید خط فرمان، می توانید از این راهنما در پرایمر خط فرمان لینوکس دیدن کنید
- درک اولیه از برنامه نویسی پایتون.
- پایتون 3.7 یا بالاتر روی سیستم اوبونتو شما نصب شده است. برای یادگیری نحوه اجرای اسکریپت پایتون در اوبونتو، می توانید به آموزش ما در مورد نحوه اجرای اسکریپت پایتون در اوبونتو مراجعه کنید.
تنظیم محیط فلاسک شما
اوبونتو 24.04 پایتون 3 را به صورت پیش فرض ارائه می کند. ترمینال را باز کنید و دستور زیر را برای بررسی مجدد نصب پایتون 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
پس از نصب پیپ، اجازه دهید Flask را نصب کنیم.
شما Flask را از طریق پیپ نصب خواهید کرد. توصیه می شود این کار را در یک محیط مجازی انجام دهید تا از تداخل با سایر بسته های سیستم خود جلوگیری کنید.
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
برای بررسی این نقطه پایانی کند، از دستور زمان در لینوکس استفاده می کنیم. دستور time برای اندازه گیری زمان اجرای یک دستور یا برنامه خاص استفاده می شود. این سه اطلاعات اصلی را ارائه می دهد:
- زمان واقعی: زمان واقعی سپری شده از شروع تا پایان دستور.
- زمان کاربر: مقدار زمان CPU صرف شده در حالت کاربر.
- زمان سیستم: مقدار زمان CPU صرف شده در حالت هسته.
این به ما کمک می کند زمان واقعی را که توسط نقطه پایانی آهسته ما گرفته می شود اندازه گیری کنیم. خروجی ممکن است چیزی شبیه به این باشد:
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
پاسخ این درخواست حدود 2 ثانیه طول می کشد، زیرا تماس 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 اجازه می دهد تا چندین درخواست به طور همزمان با استفاده از چندین فرآیند کارگر پردازش شوند.
- Load Balancing: درخواست های دریافتی را در فرآیندهای کارگر متعادل می کند و استفاده بهینه از منابع سرور را تضمین می کند.
- کارگران ناهمزمان: با کارگران ناهمزمان مانند gevent، می تواند به طور موثر وظایف طولانی مدت را بدون مسدود کردن سایر درخواست ها انجام دهد.
- مقیاسپذیری: Gunicorn میتواند با افزایش تعداد فرآیندهای کارگر برای رسیدگی به درخواستهای همزمان بیشتر، مقیاس افقی را انجام دهد.
- تحمل خطا: به طور خودکار کارگرانی را که پاسخگو یا خراب شده اند جایگزین می کند و در دسترس بودن بالا را تضمین می کند.
- Production-Ready: برخلاف سرور توسعه Flask، Gunicorn برای محیط های تولید با ویژگی های امنیتی، پایداری و عملکرد بهتر بهینه شده است.
با جابجایی به Gunicorn برای تولید، می توانید به طور قابل توجهی توان عملیاتی و پاسخگویی برنامه Flask خود را بهبود بخشید و آن را برای مدیریت کارآمد ترافیک دنیای واقعی آماده کنید.
برای کاهش بار، کش را فعال کنید
کش کردن یکی از بهترین راهها برای بهبود عملکرد Flask با کاهش پردازش اضافی است. در اینجا، شما Flask-Caching را برای کش کردن نتیجه مسیر آهسته / اضافه میکنید.
Flask-Caching را با Redis نصب و پیکربندی کنید
بسته های لازم را نصب کنید:
برای افزودن حافظه پنهان به مسیر آهسته / app.py را به روز کنید
ویرایشگر را باز کنید و فایل 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 Redis در پلتفرم برنامه، می توانید درباره یکپارچه سازی DigitalOcean Managed Redis در یک برنامه سطح تولید اطلاعات بیشتری کسب کنید.
برای بررسی اینکه آیا دادهها در حافظه پنهان ذخیره میشوند، دستورات زیر را برای نقطه پایانی /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
این یک درخواست بعدی به نقطه پایانی / کند در عرض 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 و جاوا اسکریپت به میزان قابلتوجهی کاهش دهد.
این کاهش در انتقال داده منجر به زمان بارگذاری سریعتر صفحه، بهبود تجربه کاربر و کاهش هزینههای پهنای باند میشود. علاوه بر این، بسیاری از مرورگرهای وب مدرن به طور خودکار از فشردهسازی Gzip پشتیبانی میکنند و آن را به یک تکنیک بهینهسازی کاملاً سازگار تبدیل میکنند. با فعال کردن فشردهسازی Gzip، میتوانید عملکرد و مقیاسپذیری برنامه Flask خود را بدون نیاز به تغییر در سمت کلاینت به طور موثر بهبود بخشید.
بارگذاری وظایف فشرده به Celery
برای عملیات سنگین منابع مانند ارسال ایمیل یا پردازش مجموعه داده های بزرگ، بهتر است آنها را با استفاده از Celery در کارهای پس زمینه بارگذاری کنید. این کار از مسدود کردن درخواست های دریافتی توسط وظایف طولانی مدت جلوگیری می کند.
Celery یک سیستم صف وظایف توزیع شده قدرتمند است که به شما امکان می دهد کارهای وقت گیر را به صورت ناهمزمان اجرا کنید. با بارگذاری عملیات فشرده در Celery، می توانید به طور قابل توجهی پاسخگویی و مقیاس پذیری برنامه Flask خود را بهبود بخشید. Celery با تفویض وظایف به فرآیندهای کارگری کار میکند که میتوانند روی ماشینهای جداگانه اجرا شوند و امکان استفاده بهتر از منابع و پردازش موازی را فراهم میکنند.
مزایای کلیدی استفاده از celery عبارتند از:
- بهبود زمان پاسخ برای درخواست های کاربر
- مقیاس پذیری و مدیریت منابع بهتر
- توانایی انجام کارهای پیچیده و وقت گیر بدون مسدود کردن برنامه اصلی
- پشتیبانی داخلی برای زمانبندی کار و تلاش مجدد وظایف ناموفق
- ادغام آسان با کارگزاران پیام های مختلف مانند RabbitMQ یا Redis
با استفاده از Celery، می توانید اطمینان حاصل کنید که برنامه Flask شما حتی زمانی که با وظایف محاسباتی فشرده یا I/O-bound سر و کار دارید، پاسخگو باقی می ماند.
Celery را برای کارهای پس زمینه تنظیم کنید
بیایید 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 worker را راه اندازی کنید:
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
این کار تقریباً فوراً «Task started» را برمیگرداند، حتی اگر کار پسزمینه هنوز در حال اجرا باشد.
تابع start_task() دو کار انجام می دهد:
- ()long_task.delay را فراخوانی می کند که به صورت ناهمزمان کار Celery را شروع می کند. این بدان معناست که کار در صف قرار میگیرد تا در پسزمینه اجرا شود، اما تابع منتظر تکمیل آن نمیماند.
- بلافاصله رشته «Task started» را برمی گرداند.
نکته مهمی که باید به آن توجه کرد این است که کار طولانی مدت واقعی (شبیهسازی شده توسط خواب 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 را برای رسیدگی به سایر درخواستها آزاد میکند.
در یک محیط تولید، اجرای کرفس شامل موارد زیر است:
- استفاده از یک واسطه پیام قوی مانند RabbitMQ
- استفاده از یک باطن نتیجه اختصاصی (به عنوان مثال، PostgreSQL)
- مدیریت کارگران با سیستم های کنترل فرآیند (به عنوان مثال، سرپرست)
- پیاده سازی ابزارهای نظارتی (به عنوان مثال، گل)افزایش رسیدگی و ثبت خطاها
- افزایش رسیدگی و ثبت خطاها
- استفاده از اولویت بندی وظایف
- مقیاس بندی با چندین کارگر در ماشین های مختلف
- حصول اطمینان از اقدامات امنیتی مناسب
نتیجه
در این آموزش، نحوه بهینه سازی اپلیکیشن Flask را با پیاده سازی تکنیک های مختلف افزایش عملکرد یاد گرفتید. با دنبال کردن این مراحل، میتوانید عملکرد، مقیاسپذیری و پاسخدهی برنامه Flask خود را بهبود ببخشید و از اجرای کارآمد آن حتی در بارهای سنگین اطمینان حاصل کنید.