Sử dụng Blueprint để tổ chức mã nguồn
Blueprint trong Flask là một công cụ mạnh mẽ giúp phân chia ứng dụng thành các thành phần nhỏ hơn, độc lập và dễ quản lý. Nó đóng vai trò như một bản thiết kế (blueprint) để đăng ký các route, template và static file vào ứng dụng chính.
1. Cách khởi tạo và sử dụng
Quy trình triển khai Blueprint bao gồm việc định nghĩa các route trên một đối tượng Blueprint và sau đó đăng ký đối tượng này vào ứng dụng chính (app).
# Tại file auth_routes.py
from flask import Blueprint
# Khởi tạo instance Blueprint
# 'auth': Tên định danh của blueprint
# __name__: Gói chứa blueprint
# static_folder: Thư mục chứa tài nguyên tĩnh riêng (tùy chọn)
auth_mod = Blueprint('auth_module', __name__, url_prefix='/auth')
@auth_mod.route('/login')
def login_view():
return "Trang đăng nhập"
@auth_mod.route('/register')
def register_view():
return "Trang đăng ký"
# Tại file main.py (nơi khởi tạo Flask app)
from flask import Flask
from .auth_routes import auth_mod
app = Flask(__name__)
# Đăng ký blueprint vào ứng dụng chính
app.register_blueprint(auth_mod)
2. Quy hoạch thư mục dự án
Tùy theo quy mô dự án, cấu trúc thư mục sẽ thay đổi để đảm bảo tính module hóa:
- Dự án nhỏ: Các Blueprint có thể chia theo tính năng như
user.py,product.pynằm chung trong một thư mụcviews. - Dự án lớn: Mỗi Blueprint sẽ là một package riêng biệt (thư mục có file
__init__.py), chứa riêngtemplates,staticvàmodels.pycủa chính nó.
Đối tượng g (Global Context)
Đối tượng g trong Flask là một vật chứa (container) tạm thời được sử dụng để lưu trữ dữ liệu trong suốt vòng đời của một request. Mỗi request sẽ có một đối tượng g riêng biệt.
1. Đặc điểm và ứng dụng
- Dữ liệu trong
gsẽ bị xóa sau khi request kết thúc. - Nó giúp tránh việc truyền tham số qua quá nhiều hàm trong cùng một luồng xử lý request.
- Không sử dụng
requestđể lưu trữ dữ liệu tùy chỉnh nhằm tránh xung đột với các phương thức mặc định của framework.
2. Ví dụ thực tế
from flask import g, request
@app.before_request
def load_user_context():
# Giả lập lấy ID người dùng từ header hoặc token
user_token = request.headers.get('Authorization')
if user_token:
g.current_user = "Admin"
else:
g.current_user = "Guest"
@app.route('/profile')
def get_profile():
# Truy xuất dữ liệu từ g ở bất kỳ đâu trong request này
return f"Người dùng hiện tại: {g.current_user}"
Tối ưu hóa với Database Connection Pool
Trong môi trường production, việc mở và đóng kết nối MySQL liên tục cho mỗi request sẽ gây tốn tài nguyên và làm chậm ứng dụng. Connection Pool (nhóm kết nối) giúp duy trì một số lượng kết nối nhất định để tái sử dụng.
1. Tại sao cần Connection Pool?
- Tiết kiệm tài nguyên: Giảm chi phí thiết lập kết nối TCP/IP và xác thực MySQL.
- Tránh xung đột: Nếu dùng một kết nối toàn cục (global) duy nhất, các thread có thể ghi đè dữ liệu của nhau. Pool cung cấp cho mỗi thread một kết nối riêng biệt từ danh sách có sẵn.
2. Triển khai với DBUtils và PyMySQL
Sử dụng thư viện DBUtils để quản lý tập hợp các kết nối.
import pymysql
from dbutils.pooled_db import PooledDB
from flask import jsonify
# Khởi tạo Pool
db_config = {
'creator': pymysql,
'maxconnections': 20, # Số kết nối tối đa
'mincached': 5, # Số kết nối rảnh tối thiểu khi khởi tạo
'blocking': True, # Chờ nếu hết kết nối khả dụng
'host': '127.0.0.1',
'port': 3306,
'user': 'root',
'password': 'password123',
'database': 'shop_db',
'charset': 'utf8mb4'
}
pool = PooledDB(**db_config)
@app.route('/products')
def list_products():
# Lấy một kết nối từ pool
conn = pool.connection()
cursor = conn.cursor(pymysql.cursors.DictCursor)
try:
cursor.execute('SELECT id, name, price FROM products LIMIT 10')
data = cursor.fetchall()
return jsonify(data)
finally:
# Trả kết nối về pool thay vì đóng hẳn
cursor.close()
conn.close()
3. Kiểm tra hiệu năng
Khi thực hiện stress test (kiểm tra áp lực) bằng cách gửi hàng trăm request đồng thời, việc sử dụng Pool giúp số lượng Threads_connected trong MySQL duy trì ổn định ở mức maxconnections đã cấu hình, thay vì tăng vọt và gây lỗi "Too many connections".
Để kiểm tra trạng thái kết nối trên MySQL, sử dụng lệnh:
SHOW STATUS LIKE 'Threads_connected';