Tối ưu hóa xử lý session với Redis cho ứng dụng Python dùng MySQL trên Ubuntu 22.04

Tăng tốc xử lý session trong ứng dụng Python MySQL với Redis trên Ubuntu 22.04

Xác thực người dùng là một bước quan trọng trong quá trình đăng nhập, giúp hệ thống kiểm tra danh tính và cấp quyền truy cập. Khi người dùng nhập thông tin đăng nhập (tên đăng nhập và mật khẩu), ứng dụng sẽ đối chiếu dữ liệu này với thông tin đã lưu trong cơ sở dữ liệu. Nếu thông tin hợp lệ, hệ thống sẽ cho phép truy cập.

Trong nhiều ứng dụng web, dữ liệu xác thực thường được lưu trữ trong các hệ quản trị cơ sở dữ liệu quan hệ như MySQL hoặc PostgreSQL mà không sử dụng bộ nhớ đệm. Tuy nhiên, phương pháp này có một số hạn chế:

  • Tăng tải cho cơ sở dữ liệu: Mỗi lần người dùng đăng nhập, ứng dụng phải gửi yêu cầu xác thực đến máy chủ cơ sở dữ liệu, gây áp lực lên hệ thống, đặc biệt khi có nhiều truy vấn đồng thời.

  • Khó mở rộng quy mô: Khi số lượng yêu cầu tăng cao (hàng nghìn lượt đăng nhập mỗi giây), cơ sở dữ liệu dựa trên ổ đĩa có thể không xử lý kịp, làm giảm hiệu suất toàn hệ thống.

Để giải quyết những vấn đề trên, Redis là một giải pháp hiệu quả giúp tối ưu xác thực người dùng bằng cách lưu trữ thông tin đăng nhập trong bộ nhớ đệm. Điều này giúp ứng dụng truy xuất dữ liệu nhanh hơn, giảm tải đáng kể cho cơ sở dữ liệu gốc.

Redis là một hệ thống lưu trữ dữ liệu key-value hiệu suất cao, hoạt động trên bộ nhớ RAM, giúp tăng tốc xử lý phiên đăng nhập. Trong hướng dẫn này, bạn sẽ tìm hiểu cách tích hợp Redis vào ứng dụng Python/MySQL trên Ubuntu 22.04 để cải thiện tốc độ xác thực và tối ưu hiệu suất hệ thống.

Yêu cầu trước khi bắt đầu

Trước khi bắt đầu hướng dẫn, bạn cần chuẩn bị các bước sau:

Bước 1 – Cài đặt các trình điều khiển cơ sở dữ liệu Python cho Redis và MySQL

Ứng dụng này sẽ lưu trữ vĩnh viễn thông tin đăng nhập của người dùng, như tên và mật khẩu, trong máy chủ cơ sở dữ liệu MySQL. Khi người dùng đăng nhập, một script Python sẽ truy vấn cơ sở dữ liệu MySQL và so khớp thông tin với dữ liệu đã lưu. Sau đó, script Python sẽ lưu bộ nhớ đệm thông tin đăng nhập của người dùng trong cơ sở dữ liệu Redis để phục vụ các yêu cầu sau này.

Để hoàn thành quy trình này, các script Python của bạn cần có trình điều khiển cơ sở dữ liệu (các module Python) để giao tiếp với máy chủ MySQL và Redis. Thực hiện các bước sau:

Cập nhật chỉ mục thông tin gói và chạy lệnh sau để cài đặt python3-pip, trình quản lý gói Python giúp bạn cài đặt các module bổ sung không có sẵn trong thư viện tiêu chuẩn của Python.

sudo apt install python3-pip

Cài đặt trình điều khiển MySQL cho Python:

pip install mysql-connector-python

Cài đặt trình điều khiển Redis cho Python:

pip install redis

Sau khi cài đặt các trình điều khiển cần thiết để giao tiếp với MySQL và Redis, hãy chuyển sang bước tiếp theo để khởi tạo cơ sở dữ liệu MySQL.

Bước 2 – Thiết lập một cơ sở dữ liệu MySQL mẫu

Trong hướng dẫn này, bạn sẽ cần một bảng MySQL. Trong môi trường sản xuất, bạn có thể có hàng chục bảng phục vụ các yêu cầu khác nhau. Thiết lập một cơ sở dữ liệu và tạo bảng bằng cách thực hiện các lệnh sau:

Đăng nhập vào máy chủ cơ sở dữ liệu MySQL với tư cách người dùng root:

sudo mysql -u root -p

Nhập mật khẩu root của MySQL khi được yêu cầu, sau đó chạy các lệnh sau để tạo cơ sở dữ liệu mẫu company và tài khoản company_user. Hãy thay thế example-mysql-password bằng mật khẩu mạnh:

CREATE DATABASE company;
CREATE USER 'company_user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'example-mysql-password';
GRANT ALL PRIVILEGES ON company.* TO 'company_user'@'localhost';
FLUSH PRIVILEGES;

Bạn sẽ nhận được thông báo sau để xác nhận các lệnh đã chạy thành công:

OutputQuery OK, 1 row affected (0.01 sec)

Chuyển sang cơ sở dữ liệu company:

USE company;

Xác nhận rằng bạn đã kết nối thành công đến cơ sở dữ liệu mới với thông báo:

OutputDatabase changed

Tạo bảng system_users. Cột user_id sẽ đóng vai trò PRIMARY KEY để định danh duy nhất cho mỗi người dùng. Các cột usernamepassword chứa thông tin đăng nhập mà người dùng cần cung cấp để đăng nhập ứng dụng. Các cột first_namelast_name lưu trữ tên của người dùng:

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;

Xác nhận rằng bảng mới đã được tạo thành công với thông báo:

OutputQuery OK, 0 rows affected (0.03 sec)

Chèn dữ liệu mẫu vào bảng system_users. Sử dụng hàm MD5(…) tích hợp của MySQL để băm mật khẩu cho mục đích bảo mật:

INSERT INTO system_users (username, first_name, last_name, password) VALUES ('john_doe', 'JOHN', 'DOE', MD5('password_1'));
INSERT INTO system_users (username, first_name, last_name, password) VALUES ('mary_henry', 'MARY', 'HENRY', MD5('password_2'));
INSERT INTO system_users (username, first_name, last_name, password) VALUES ('peter_jade', 'PETER', 'JADE', MD5('password_3'));

Xác nhận kết quả với thông báo:

OutputQuery OK, 1 row affected (0.00 sec)

Truy vấn bảng system_users để đảm bảo dữ liệu đã được chèn đúng:

SELECT
    user_id,
    first_name,
    last_name,
    password
FROM system_users;

Kết quả truy vấn sẽ như sau:

Output+---------+------------+-----------+----------------------------------+
| user_id | first_name | last_name | password                         |
+---------+------------+-----------+----------------------------------+
|       1 | JOHN       | DOE       | 57210b12af5e06ad2e6e54a93b1465aa |
|       2 | MARY       | HENRY     | 259640f97ac2b4379dd540ff4016654c |
|       3 | PETER      | JADE      | 48ef85c894a06a4562268de8e4d934e1 |
+---------+------------+-----------+----------------------------------+
3 rows in set (0.00 sec)

Đăng xuất khỏi máy chủ MySQL:

QUIT;

Bạn đã thiết lập thành công cơ sở dữ liệu MySQL cho ứng dụng. Trong bước tiếp theo, bạn sẽ xây dựng module Python để giao tiếp với cơ sở dữ liệu mẫu.

Bước 3 – Tạo Module cổng MySQL trung tâm cho Python

Khi viết bất kỳ dự án Python nào, bạn nên tạo một module riêng cho từng tác vụ nhằm tăng khả năng tái sử dụng mã nguồn. Trong bước này, bạn sẽ thiết lập một module trung tâm cho phép kết nối và truy vấn cơ sở dữ liệu MySQL từ một script Python. Thực hiện các bước sau:

Tạo một thư mục dự án. Thư mục này sẽ tách riêng các file mã nguồn Python của bạn khỏi các file hệ thống khác:

Chuyển sang thư mục dự án mới:

mkdir project

Sử dụng trình soạn thảo nano để mở file mysql_db.py, file này sẽ chứa module Python giao tiếp với cơ sở dữ liệu MySQL:

cd project

Nhập nội dung sau vào file mysql_db.py. Thay thế example-mysql-password bằng mật khẩu MySQL đúng của tài khoản company_user:

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]

Lưu và đóng file mysql_db.py.

Module mysql_db.py có một lớp (MysqlDb) với hai phương thức:

  • db_con(self): Kết nối tới cơ sở dữ liệu company đã tạo ở trên và trả về một kết nối MySQL có thể tái sử dụng.

  • query(self, username, password): Nhận usernamepassword, truy vấn bảng system_users để kiểm tra sự khớp. Nếu không tìm thấy người dùng, trả về False; nếu tìm thấy, trả về mật khẩu (result[1]).

Tiếp theo, hãy thiết lập một module Redis tương tự để giao tiếp với kho dữ liệu key-value Redis.

Bước 4 – Tạo Module Redis trung tâm cho Python

Trong bước này, bạn sẽ lập trình một module để kết nối với máy chủ Redis. Thực hiện các bước sau:

Mở file mới redis_db.py:

nano redis_db.py

Nhập nội dung sau vào file redis_db.py. Thay thế example-redis-password bằng mật khẩu đúng cho máy chủ Redis:

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

Lưu và đóng file redis_db.py.

File trên có một lớp (RedisDb) với phương thức db_con(self), sử dụng thông tin xác thực được cung cấp để kết nối tới máy chủ Redis và trả về kết nối có thể tái sử dụng.

Sau khi thiết lập module Redis, hãy tạo file chính cho dự án của bạn.

Bước 5 – Tạo điểm vào cho ứng dụng

Mỗi ứng dụng Python đều cần có một điểm vào hoặc file chính để thực thi khi chạy ứng dụng. Trong file này, bạn sẽ viết mã hiển thị thời gian hiện tại của máy chủ cho người dùng đã xác thực. File này sử dụng các module MySQL và Redis tùy chỉnh mà bạn đã tạo để xác thực người dùng. Thực hiện các bước sau để tạo file:

Mở file mới index.py:

nano index.py

Nhập nội dung sau vào file 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": "Invalid username/password."}
            else:
                resp = {"time": current_time, "authorized by": "Redis server"}
        else:
            mysql_resp  = mysql_server.query(auth_user, auth_password)
            if mysql_resp == False:
                 resp =  {"error": "Invalid username/password."}
            else:
                resp = {"time": current_time, "authorized by": "MySQL server"}
                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("Web server is running on port 8080...")

try:
    httpd.serve_forever()
except KeyboardInterrupt:
    httpd.server_close()
    print("Web server has stopped runing.")

Lưu và đóng file index.py.

Trong file index.py:

  • Phần import thêm các module cần thiết:

    • utf_8, base64, md5, và json để xử lý mã hóa và định dạng văn bản.

    • http.server, HTTPStatus, và socketserver để xây dựng máy chủ web.

    • datetime để xử lý ngày giờ.

    • mysql_dbredis_db là các module tùy chỉnh mà bạn đã tạo để truy cập MySQL và Redis.

  • Lớp HttpHandler(http.server.SimpleHTTPRequestHandler) xử lý các yêu cầu HTTP GET và hiển thị thời gian hiện tại của máy chủ cho người dùng đã xác thực.

  • Trong phần kiểm tra, nếu thông tin người dùng tồn tại trong Redis (if redis_client.exists(auth_user):), sẽ kiểm tra xem mật khẩu được lưu trong Redis có khớp với mật khẩu người dùng cung cấp hay không. Nếu không khớp, trả về thông báo lỗi "Invalid username/password."; nếu khớp, trả về thời gian hiện tại kèm thông tin xác thực từ Redis.

  • Nếu thông tin người dùng không tồn tại trong máy chủ Redis, ứng dụng sẽ truy vấn máy chủ cơ sở dữ liệu MySQL bằng lệnh mysql_resp = mysql_server.query(auth_user, auth_password). Trong trường hợp mật khẩu người dùng cung cấp không khớp với giá trị đã lưu trong cơ sở dữ liệu, ứng dụng sẽ trả về lỗi {"error": "Invalid username/password."}. Ngược lại, ứng dụng sẽ lưu bộ nhớ đệm thông tin người dùng trong máy chủ Redis bằng lệnh redis_client.set(auth_user, mysql_resp).

    Trong tất cả các trường hợp mà thông tin đăng nhập của người dùng khớp với dữ liệu trong Redis/MySQL, ứng dụng sẽ hiển thị thời gian hiện tại của hệ thống bằng lệnh {"time": current_time, ...}. Mục "authorized by" trong kết quả cho bạn biết máy chủ cơ sở dữ liệu nào đã xác thực người dùng trong ứng dụng.

    if redis_client.exists(auth_user):
          if md5(auth_password.encode('utf8')).hexdigest() != redis_client.get(auth_user).decode('utf8'):
              resp = {"error": "Invalid username/password."}
          else:
              resp = {"time": current_time, "authorized by": "Redis server"}
      else:
          mysql_resp  = mysql_server.query(auth_user, auth_password)
          if mysql_resp == False:
               resp =  {"error": "Invalid username/password."}
          else:
              resp = {"time": current_time, "authorized by": "MySQL server"}
              redis_client.set(auth_user, mysql_resp)

    Bạn đã thiết lập xong file chính cho ứng dụng. Trong bước tiếp theo, bạn sẽ kiểm tra ứng dụng.

Bước 6 – Kiểm tra ứng dụng

Trong bước này, bạn sẽ chạy ứng dụng để kiểm tra cơ chế lưu bộ nhớ đệm của Redis có hoạt động hay không. Thực hiện các lệnh sau:

Chạy ứng dụng bằng lệnh:

python3 index.py

Xác nhận rằng máy chủ web tùy chỉnh của ứng dụng đang chạy với thông báo:

OutputWeb server is running on port 8080...

Mở một kết nối SSH khác đến máy chủ của bạn trong một cửa sổ terminal mới và chạy các lệnh curl sau để gửi bốn yêu cầu GET sử dụng thông tin đăng nhập của john_doe. Thêm [1-4] vào cuối URL http://localhost:8080/ để gửi bốn yêu cầu trong một lệnh:

curl -X GET -u john_doe:password_1  http://localhost:8080/[1-4]

Xác nhận kết quả như sau. Máy chủ MySQL chỉ phục vụ yêu cầu xác thực đầu tiên, sau đó cơ sở dữ liệu Redis phục vụ 3 yêu cầu tiếp theo.

Output[1/4]
{
  "time": "2023-11-07 10:04:38",
  "authorized by": "MySQL server"
}
[4/4]
{
  "time": "2023-11-07 10:04:38",
  "authorized by": "Redis server"
}

Ứng dụng của bạn đã hoạt động đúng như mong đợi.

Kết luận

Trong hướng dẫn này, bạn đã xây dựng một ứng dụng Python sử dụng máy chủ Redis để lưu bộ nhớ đệm thông tin đăng nhập của người dùng. Redis là một máy chủ cơ sở dữ liệu có khả năng sẵn sàng cao và mở rộng quy mô, có thể xử lý hàng nghìn giao dịch mỗi giây. Với cơ chế lưu bộ nhớ đệm của Redis, bạn có thể giảm đáng kể lưu lượng truy cập tới máy chủ cơ sở dữ liệu phía sau.

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *