Как ускорить обработку сеанса приложения Python/MySQL с использованием Redis на Ubuntu 22.04

Введение

Аутентификация – это процесс проверки личности пользователей во время запросов на вход. В процессе аутентификации пользователи предоставляют свои учетные данные в виде имен пользователей и паролей. Затем приложение сопоставляет эти учетные данные с записями, хранящимися в базе данных. При наличии соответствия приложение предоставляет пользователям доступ к системе.

Хранение учетных данных в реляционной базе данных, такой как MySQL или PostgreSQL, без механизма кэширования, по-прежнему является распространенным и практичным подходом, но он сопряжен с определенными ограничениями:

  • Перегрузка базы данных. Приложение должно сделать круговой обра тно-поступательный запрос к серверу базы данных для проверки учетных данных пользователей из таблицы базы данных при каждом запросе на вход пользователя. Поскольку база данных может все еще обслуживать другие запросы на чтение/запись, весь процесс перегружает базу данных и замедляет его работу.

  • У традиционных дисковых баз данных есть проблемы масштабируемости. Когда ваше приложение получает тысячи запросов в секунду, дисковые базы данных не работают оптимально.

Для преодоления вышеупомянутых проблем вы можете использовать Redis для кеширования учетных данных пользователей, чтобы ваше приложение не обращалось к базе данных каждый раз при запросе на вход. Redis является одним из самых популярных сверхбыстрых хранилищ данных, использующим оперативную память вашего компьютера для хранения данных в форме пар ключ-значение. В этом руководстве вы будете использовать базу данных Redis для ускорения обработки сеансов в вашем приложении на Python/MySQL на сервере Ubuntu 22.04.

Предварительные требования

Прежде чем начать этот учебник, вам нужно настроить следующее:

Шаг 1 — Установка драйверов базы данных Python для Redis и MySQL

Это приложение постоянно хранит учетные данные пользователей, такие как имена и пароли, в базе данных сервера MySQL. Когда пользователь входит в приложение, сценарий на языке Python запрашивает базу данных MySQL и сопоставляет детали с сохраненными значениями. Затем сценарий на языке Python кеширует учетные данные входа пользователя в базе данных Redis для обслуживания других будущих запросов. Для завершения этой логики ваши сценарии на языке Python требуют драйверы базы данных (модули Python) для взаимодействия с серверами MySQL и Redis. Следуйте указанным ниже шагам для установки драйверов:

  1. Обновите свой индекс информации о пакете и выполните следующую команду для установки python3-pip, менеджера пакетов Python, который позволяет устанавливать дополнительные модули, не входящие в стандартную библиотеку Python.
sudo apt install python3-pip
  1. Установите драйвер MySQL для Python:
pip install mysql-connector-python
  1. Установите драйвер Redis для Python:
pip install redis

После установки необходимых драйверов для взаимодействия с MySQL и Redis переходите к следующему шагу и инициализируйте базу данных MySQL.

Шаг 2 — Настройка примера базы данных MySQL

Для этого руководства вам потребуется одна таблица MySQL. В производственной среде вы можете иметь десятки таблиц, обслуживающих другие запросы. Создайте базу данных и таблицу, выполнив следующие команды:

  1. Войдите на сервер баз данных MySQL как пользователь root:

    sudo mysql -u root -p
    
  2. Введите пароль для сервера MySQL root, когда будет запрошен, и нажмите ENTER, чтобы продолжить. Затем выполните следующую команду, чтобы создать пример базы данных company и учетную запись company_user. Замените example-mysql-password на надежный пароль:

  1. CREATE DATABASE company;
  2. CREATE USER 'company_user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'example-mysql-password';
  3. GRANT ALL PRIVILEGES ON company.* TO 'company_user'@'localhost';
  4. FLUSH PRIVILEGES;
  1. Убедитесь, что вы получаете следующий вывод, чтобы подтвердить успешное выполнение предыдущих команд:

    Результат
    Query OK, 1 row affected (0.01 sec)
  2. Переключитесь на новую базу данных company:

    1. USE company;
  3. Подтвердите, что вы подключены к новой базе данных, проверив следующий вывод:

    Вывод
    База данных изменена
  4. Создайте таблицу system_users. Столбец user_id служит в качестве PRIMARY KEY для уникальной идентификации каждого пользователя. Столбцы username и password представляют собой учетные данные, которые пользователи должны предоставить для входа в приложение. Столбцы first_name и last_name хранят имена пользователей:

    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;
    
  5. Убедитесь, что вы создали новую таблицу, проверив следующий вывод:

    Вывод
    Query OK, 0 rows affected (0.03 sec)
  6. Заполните таблицу system_users тестовыми данными. Используйте встроенную функцию MD5(...) MySQL для хеширования пароля в целях безопасности:

    1. INSERT INTO system_users (username, first_name, last_name, password) VALUES ('john_doe', 'JOHN', 'DOE', MD5('password_1'));
    2. INSERT INTO system_users (username, first_name, last_name, password) VALUES ('mary_henry', 'MARY', 'HENRY', MD5('password_2'));
    3. INSERT INTO system_users (username, first_name, last_name, password) VALUES ('peter_jade', 'PETER', 'JADE', MD5('password_3'));
  7. Проверьте результат ниже:

    Вывод
    Query OK, 1 row affected (0.00 sec)
  8. Запросите таблицу system_users, чтобы удостовериться, что данные на месте:

    1. SELECT
    2. user_id,
    3. first_name,
    4. last_name,
    5. password
    6. FROM system_users;
  9. Проверьте следующий вывод:

    Вывод
    +---------+------------+-----------+----------------------------------+ | user_id | first_name | last_name | password | +---------+------------+-----------+----------------------------------+ | 1 | JOHN | DOE | 57210b12af5e06ad2e6e54a93b1465aa | | 2 | MARY | HENRY | 259640f97ac2b4379dd540ff4016654c | | 3 | PETER | JADE | 48ef85c894a06a4562268de8e4d934e1 | +---------+------------+-----------+----------------------------------+ 3 строки в наборе (0.00 сек)
  10. Выйти из базы данных MySQL:

    1. QUIT;

Теперь вы настроили правильную базу данных MySQL для вашего приложения. На следующем шаге вы создадите модуль Python, взаимодействующий с вашей примерной базой данных.

Шаг 3 — Создание центрального модуля шлюза MySQL для Python

При написании любого проекта на Python вы должны создать отдельный модуль для каждой задачи, чтобы обеспечить повторное использование кода. На этом шаге вы настроите центральный модуль, который позволит вам подключаться и выполнять запросы к базе данных MySQL из скрипта на Python. Следуйте инструкциям ниже:

  1. Создайте каталог project. Этот каталог отделяет ваши файлы исходного кода Python от остальных системных файлов:

    1. mkdir project
  2. Перейдите в новый каталог project:

    1. cd project
  3. Используйте текстовый редактор nano, чтобы открыть новый файл mysql_db.py. В этом файле находится модуль Python, взаимодействующий с базой данных MySQL:

    nano mysql_db.py
    
  4. Введите следующую информацию в файл mysql_db.py. Замените example-mysql-password на правильный пароль MySQL для учетной записи company_user:

    ~/project/mysql_db.py
    
    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]
    
  5. Сохраните и закройте файл mysql_db.py.

Модульный файл mysql_db.py содержит один класс (MysqlDb:) с двумя методами:
db_con(self): подключается к созданной ранее базе данных company и возвращает повторно используемое соединение MySQL с помощью оператора return mysql_con.
query(self, username, password): – метод, который принимает имя пользователя (username) и пароль (password) и запрашивает таблицу system_users, чтобы определить, существует ли совпадение. Условный оператор if row_count < 1: ... else: return result[1] возвращает логическое значение False, если пользователя нет в таблице, или пароль пользователя (result[1]), если приложение находит совпадение.

После того, как модуль MySQL готов, перейдите к следующему шагу для настройки аналогичного модуля Redis, который взаимодействует с хранилищем ключ-значение Redis.

Шаг 4 — Создание центрального модуля Redis для Python

На этом шаге вы напишете модуль, который подключается к серверу Redis. Выполните следующие шаги:

  1. Откройте новый файл redis_db.py:

    nano redis_db.py
    
  2. Введите следующую информацию в файл redis_db.py. Замените example-redis-password на правильный пароль для сервера Redis:

    ~/project/redis_db.py
    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
    
  3. Сохраните и закройте файл redis_db.py.

  • В вышеприведенном файле есть один класс (RedisDb:).

  • Под этим классом метод db_con(self): использует предоставленные учетные данные для подключения к серверу Redis и возвращает повторно используемое соединение с помощью оператора return redis_con.

После настройки класса Redis перейдите к следующему шагу, создав основной файл для вашего проекта.

Шаг 5 — Создание точки входа в приложение

У каждого приложения на Python должна быть точка входа или основной файл, который выполняется при запуске приложения. В этом файле вы создадите код, который показывает текущее время сервера для аутентифицированных пользователей. Этот файл использует пользовательские модули MySQL и Redis, созданные вами, для аутентификации пользователей. Чтобы создать файл, выполните следующие шаги:

  1. Откройте новый файл index.py:

    nano index.py
    
  2. Введите следующую информацию в файл index.py:

    ~/project/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": "Неверное имя пользователя или пароль."}
                else:
                    resp = {"time": current_time, "authorized by": "Сервер Redis"}
            else:
                mysql_resp  = mysql_server.query(auth_user, auth_password)
                if mysql_resp == False:
                     resp =  {"error": "Неверное имя пользователя или пароль."}
                else:
                    resp = {"time": current_time, "authorized by": "Сервер MySQL"}
                    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("Веб-сервер работает на порту 8080...")
    
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        httpd.server_close()
        print("Веб-сервер остановлен.")
    
  3. Сохраните и закройте файл index.py.

  • В файле index.py в разделе import... добавьте следующие модули в ваш проект:

    • utf_8, base64, md5 и json, модули кодирования и форматирования текста.

    • http.server, HTTPStatus и socketserver, модули веб-сервера.

    • datetime, модуль времени/даты.

    • mysql_db и redis_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уют, при в серверложеение Redis зап,ра пришложивениеает сервер зап базраы данныхш MySQLив,ает использ серверу базяы данных опер MySQLатор с < использованиcodeем>mysql оператор_respа = < mysqlcode_server>.querymysql_resp (auth = mysql_server_user.query(auth,_user auth,_password auth).code В случ>. Вае случ еслиае если предоставленный пользовател предостемавленный пользователем пар парольоль не со неответ соответствуетствует знач значениениюю, хран, храняящщемемуусяся в баз в базе данныхе, данных, при приложение вложозвращает ошибкуение < возвращаетcode ошибку <>{“code>{“errorerror”:”: “Invalid username/password “.”}. username/password В.” прот}. случа В преот,ив приложном случениеа кэеши приложруетение данные к пользователяэши нару серверете с Redisвед сения о использов пользователе вани сервереме опер Redisатора, использ <уcodeя> оперredisатор_client.set(auth redis,_client mysql_resp.set)., mysql_resp).

  • Во всех случаях, когда учетные данные пользователя совпадают с данными Redis/MySQL, приложение отображает текущую дату/время системы с использованием выражения {"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": "Неверное имя пользователя/пароль."}
          else:
              resp = {"time": текущее_время, "authorized by": "Сервер Redis"}
      else:
          mysql_resp  = mysql_server.query(auth_user, auth_password)
          if mysql_resp == False:
               resp =  {"error": "Неверное имя пользователя/пароль."}
          else:
              resp = {"time": текущее_время, "authorized by": "Сервер MySQL"}
              redis_client.set(auth_user, mysql_resp)   
    

Вы теперь настроили основной файл для приложения. На следующем шаге вы протестируете приложение.

Шаг 6 — Тестирование Приложения

На этом шаге вы запустите приложение, чтобы убедиться, что механизм кэширования Redis работает. Выполните нижеприведенные команды, чтобы протестировать приложение:

  1. Используйте следующую команду python3, чтобы запустить приложение:

    python3 index.py
    
  2. Убедитесь, что пользовательский веб-сервер приложения запущен:

    Вывод
    Веб-сервер запущен на порту 8080...
  3. Установите еще одно SSH-соединение с вашим сервером в новом окне терминала и выполните следующие команды curl, чтобы отправить четыре запроса GET с использованием учетных данных john_doe. Добавьте [1-4] в конце URL http://localhost:8080/, чтобы отправить четыре запроса одной командой:

    curl -X GET -u john_doe:password_1  http://localhost:8080/[1-4]
    
  4. Проверьте следующие выводы. Сервер MySQL обслуживает только первый запрос аутентификации. Затем база данных Redis обслуживает следующие три запроса.

    Вывод
    [1/4] { "time": "2023-11-07 10:04:38", "authorized by": "Сервер MySQL" } [4/4] { "time": "2023-11-07 10:04:38", "authorized by": "Сервер Redis" }

Логика вашего приложения теперь работает как ожидается.

Заключение

В этом руководстве вы создали приложение на Python, использующее сервер Redis для кэширования учетных данных пользователей. Redis – это высокодоступный и масштабируемый сервер базы данных, способный выполнять тысячи транзакций в секунду. С механизмом кэширования Redis в вашем приложении вы можете значительно снизить трафик на сервере вашей базы данных. Для получения дополнительной информации о приложениях Redis обратитесь к нашим учебным пособиям по Redis.

Source:
https://www.digitalocean.com/community/tutorials/how-to-speed-up-python-mysql-application-session-handling-with-redis-on-ubuntu-22-04