Ubuntu 上 Python/MySQL 应用会话管理的 Redis 加速指南

引言

在本指南中,您将使用 Redis 数据库来加速 Ubuntu 服务器上的 Python/MySQL 应用程序的会话处理。

前提条件

要完成本教程,你需要:

需要一个已设置的Ubuntu服务器,具有非root用户,具有sudo权限,并启用了防火墙以阻止非必要的端口。

设置完成后,以的非root用户身份登录并进行第一步。

建议使用至少有以下配置的服务器:

4 核心的 CPU,4GB 的内存

选择服务器提供商
为了本教程的演示,我将以一个具体的云服务提供商为例,展示如何进行操作。选择哪个提供商根据个人偏好和需求来决定。

以下步骤仅供参考,请根据实际需求选择配置。

购买云服务器
本示例中,我们选择了香港作为服务器区域。

点击 云产品云服务器立即购买

选择操作系统
在创建服务器实例时,选择 Ubuntu 作为操作系统。

连接到服务器
使用 X-shell 或偏好的 SSH 客户端,通过远程用户名和密码连接到服务器。成功连接后,将看到特定的欢迎信息,表明已成功登录。

https://syxoss.oss-cn-hangzhou.aliyuncs.com/Typora202411211021991.png

  • 切换到新的 sudo 用户账户并安装:
    • MySQL 服务器。
    • Redis 服务器。

第 1 步 — 安装 Python 数据库驱动程序(Redis 和 MySQL)

这个应用程序永久存储用户的凭证,如姓名和密码,在 MySQL 数据库服务器中。当用户登录应用程序时,Python 脚本查询 MySQL 数据库并将详细信息与存储的值进行匹配。然后,Python 脚本将用户的登录凭证缓存在 Redis 数据库中,以服务于其他未来的请求。为了完成该逻辑,您的 Python 脚本需要数据库驱动程序(Python 模块)与 MySQL 和 Redis 服务器进行通信。按照以下步骤安装驱动程序:

  1. 更新您的包信息索引并运行以下命令以安装 python3-pip,这是一个 Python 包管理器,允许您安装不属于 Python 标准库的额外模块。
sudo apt install python3-pip
  1. 安装 Python 的 MySQL 驱动程序:
pip install mysql-connector-python
  1. 安装 Python 的 Redis 驱动程序:
pip install redis

安装了与 MySQL 和 Redis 通信所需的驱动程序后,请进行下一步并初始化 MySQL 数据库。

第 2 步 — 设置示例 MySQL 数据库

对于本指南,您需要一个 MySQL 表。在生产环境中,您可以有数十个表来处理其他请求。通过执行以下命令设置数据库并创建表:

  1. root 用户身份登录到 MySQL 数据库服务器:
sudo mysql -u root -p
  1. 输入您的 MySQL 服务器的 root 密码,然后按 ENTER 继续。然后,运行以下命令创建一个示例 company 数据库和一个 company_user 账户。将 example-mysql-password 替换为一个强密码:
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;
  1. 确保您收到以下输出以确认之前的命令已成功运行:
OutputQuery OK, 1 row affected (0.01 sec)
  1. 切换到新的 company 数据库:
USE company;
  1. 确认您已连接到新数据库,通过验证以下输出:
OutputDatabase changed
  1. 创建一个 system_users 表。user_id 列作为 PRIMARY KEY 以唯一标识每个用户。usernamepassword 列是用户必须提交的登录凭证,以登录应用程序。first_namelast_name 列存储用户的姓名:
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;
  1. 确保您已创建新表,通过验证以下输出:
OutputQuery OK, 0 rows affected (0.03 sec)
  1. 用示例数据填充 system_users 表。使用 MySQL 内置的 MD5(...) 函数来哈希密码以确保安全:
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'));
  1. 验证以下输出:
OutputQuery OK, 1 row affected (0.00 sec)
  1. 查询 system_users 表以确保数据已就位:
SELECT
    user_id,
    first_name,
    last_name,
    password
FROM system_users;
  1. 验证以下输出:
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)
  1. 从 MySQL 数据库退出:
QUIT;

您现在已经为您的应用程序设置了正确的 MySQL 数据库。在下一步中,您将构建一个与您的示例数据库通信的 Python 模块。

第 3 步 — 为 Python 创建一个中心 MySQL 网关模块

在编写任何 Python 项目时,您应该为每个任务创建一个单独的模块,以促进代码重用。在这一步中,您将设置一个中心模块,允许您从 Python 脚本连接并查询 MySQL 数据库。按照以下步骤进行:

  1. 创建一个 project 目录。此目录将您的 Python 源代码文件与系统的其余文件分开:
mkdir project
  1. 切换到新的 project 目录:
cd project
  1. 使用 nano 文本编辑器打开一个新的 mysql_db.py 文件。此文件托管与 MySQL 数据库通信的 Python 模块:
nano mysql_db.py
  1. 将以下信息输入到 mysql_db.py 文件中。将 example-mysql-password 替换为 company_user 账户的正确 MySQL 密码:
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]
  1. 保存并关闭 mysql_db.py 文件。

mysql_db.py 模块文件有一个类(MysqlDb:)和两个方法:

  • db_con(self):,连接到您之前创建的示例 company 数据库,并返回一个可重用的 MySQL 连接。
  • query(self, username, password):,一个接受 usernamepassword 并查询 system_users 表以查找匹配项的方法。条件 if row_count < 1: ... else: return result[1] 语句在表中找不到用户时返回布尔值 False,或者如果应用程序找到匹配项,则返回用户的密码(result[1])。

有了 MySQL 模块准备就绪,按照下一步

设置一个类似的 Redis 模块,与 Redis 键值存储通信。

第 4 步 — 为 Python 创建一个中心 Redis 模块

在这一步中,您将编写一个模块,连接到 Redis 服务器。执行以下步骤:

  1. 打开一个新的 redis_db.py 文件:
nano redis_db.py
  1. 将以下信息输入到 redis_db.py 文件中。将 example-redis-password 替换为 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
  1. 保存并关闭 redis_db.py 文件。
  • 上述文件有一个类(RedisDb:)。
  • 在这个类下,db_con(self): 方法使用提供的凭据连接到 Redis 服务器,并返回一个可重用的连接。

设置好 Redis 类后,下一步创建项目的主文件。

第 5 步 — 创建应用程序的入口点

每个 Python 应用程序都必须有一个入口点或主文件,当应用程序运行时执行。在该文件中,您将创建一个代码,显示当前服务器的时间给经过身份验证的用户。该文件使用您创建的自定义 MySQL 和 Redis 模块来验证用户。按照以下步骤创建文件:

  1. 打开一个新的 index.py 文件:
nano index.py
  1. 将以下信息输入到 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 running.")
  1. 保存并关闭 index.py 文件。
  • index.py 文件中,import... 部分将以下模块添加到您的项目中:
    • utf_8, base64, md5, 和 json,文本编码和格式化模块。
    • http.server, HTTPStatus, 和 socketserver,Web 服务器模块。
    • datetime,时间/日期模块。
    • mysql_dbredis_db,您之前创建的自定义模块,用于访问 MySQL 和 Redis 服务器。
  • HttpHandler(http.server.SimpleHTTPRequestHandler): 是 HTTP 服务器的处理程序类。在类下,do_GET(self): 方法服务 HTTP GET 请求,并为经过身份验证的用户显示系统的日期/时间。
  • if ... : else: ... 逻辑中,Python 脚本运行逻辑 if redis_client.exists(auth_user): 语句以检查用户凭证是否存在于 Redis 服务器中。如果用户详细信息存在且 Redis 存储的密码与用户提交的密码不匹配,应用程序返回 {"error": "Invalid username/password."} 错误。

如果用户详细信息不在 Redis 服务器中,应用程序使用 mysql_resp = mysql_server.query(auth_user, auth_password) 语句查询 MySQL 数据库服务器。如果用户提供的密码与数据库存储的值不匹配,应用程序返回 {"error": "Invalid username/password."} 错误。否则,应用程序使用 redis_client.set(auth_user, mysql_resp) 语句将用户详细信息缓存在 Redis 服务器中。

  • 在所有用户凭证与 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": "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)

您现在已经为应用程序设置了主文件。在下一步中,您将测试应用程序。

第 6 步 — 测试应用程序

在这一步中,您将运行您的应用程序以查看 Redis 缓存机制是否有效。执行以下命令以测试应用程序:

  1. 使用以下 python3 命令运行应用程序:
python3 index.py
  1. 确保应用程序的自定义 Web 服务器正在运行:
OutputWeb server is running on port 8080...
  1. 在新终端窗口中建立另一个 SSH 连接到您的服务器,并运行以下 curl 命令,使用 john_doe's 凭证发送四个 GET 请求。在 http://localhost:8080/ URL 后附加 [1-4] 以在一个命令中发送四个请求:
curl -X GET -u john_doe:password_1  <http://localhost:8080/[1-4]>

您的应用程序逻辑现在按预期工作。

结论

在本指南中,您构建了一个使用 Redis 服务器缓存用户登录凭证的 Python 应用程序。Redis 是一个高可用且可扩展的数据库服务器,可以每秒执行数千次事务。通过在应用程序中使用 Redis 缓存机制,您可以大幅减少后端数据库服务器的流量。