Como acelerar o tratamento de sessões de aplicativos Python/MySQL com Redis no Ubuntu 22.04
O autor selecionou a Apache Software Foundation para receber uma doação como parte do programa Write for DOnations.
Introdução
Autenticação é o processo de verificação da identidade dos usuários durante solicitações de login. Em um processo de autenticação, os usuários enviam suas credenciais como nomes de usuário e senhas. Em seguida, o aplicativo combina essas credenciais de login com as entradas armazenadas do banco de dados. O aplicativo concede aos usuários acesso ao sistema se houver correspondência.
Armazenar credenciais de login em um banco de dados relacional como MySQL ou PostgreSQL sem um mecanismo de cache ainda é uma abordagem comum e prática, mas apresenta as seguintes limitações:
Sobrecarregando o banco de dados. O aplicativo deve fazer uma viagem de ida e volta ao servidor de banco de dados para verificar as credenciais dos usuários em uma tabela de banco de dados sempre que um usuário enviar uma solicitação de login. Como o banco de dados ainda pode atender a outras solicitações de leitura/gravação, todo o processo sobrecarrega o banco de dados e o torna lento.
Os bancos de dados tradicionais baseados em disco apresentam problemas de escalabilidade. Quando seu aplicativo recebe milhares de solicitações por segundo, os bancos de dados baseados em disco não funcionam de maneira ideal.
Para superar os desafios acima, você pode usar o Redis para armazenar em cache as credenciais de login dos usuários para que seu aplicativo não precise entrar em contato com o banco de dados back-end durante cada solicitação de login. Redis é um dos armazenamentos de dados ultrarrápidos mais populares que utiliza a RAM do seu computador para armazenar dados em pares de valores-chave. Neste guia, você usará o banco de dados Redis para acelerar o tratamento de sessões em seu aplicativo Python/MySQL no servidor Ubuntu 22.04.
Pré-requisitos
Antes de começar este tutorial, você precisará configurar o seguinte:
Implante um servidor Ubuntu 22.04.
Configure e inicialize o servidor.
Crie um usuário
sudo
não raiz.Mude para a nova conta de usuário
sudo
e instale:Servidor MySQL.
Servidor Redis.
Passo 1 — Instalando drivers de banco de dados Python para Redis e MySQL
Este aplicativo armazena permanentemente as credenciais dos usuários, como nomes e senhas, em um servidor de banco de dados MySQL. Quando um usuário faz login no aplicativo, um script Python consulta o banco de dados MySQL e compara os detalhes com os valores armazenados. Em seguida, o script Python armazena em cache as credenciais de login do usuário em um banco de dados Redis para atender outras solicitações futuras. Para completar essa lógica, seus scripts Python exigem drivers de banco de dados (módulos Python) para se comunicarem com os servidores MySQL e Redis. Siga as etapas abaixo para instalar os drivers:
- Atualize o índice de informações do seu pacote e execute o seguinte comando para instalar
python3-pip
, um gerenciador de pacotes Python que permite instalar módulos adicionais que não fazem parte da biblioteca padrão do Python.
sudo apt install python3-pip
- Instale o driver MySQL para Python:
pip install mysql-connector-python
- Instale o driver Redis para Python:
pip install redis
Após instalar os drivers necessários para comunicação com MySQL e Redis, prossiga para a próxima etapa e inicialize um banco de dados MySQL.
Passo 2 — Configurando um exemplo de banco de dados MySQL
Para este guia, você precisa de uma tabela MySQL. Em um ambiente de produção, você pode ter dezenas de tabelas que atendem a outras solicitações. Configure um banco de dados e crie a tabela executando os seguintes comandos:
Faça login no servidor de banco de dados MySQL como usuário
root
:sudo mysql -u root -p
Digite a senha
root
do seu servidor MySQL quando solicitado e pressioneENTER
para continuar. Em seguida, execute o comando a seguir para criar um banco de dadoscompany
de amostra e uma contacompany_user
. Substituaexample-mysql-password
por uma senha forte:
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;
Certifique-se de receber a seguinte saída para confirmar se os comandos anteriores foram executados com êxito:
Query OK, 1 row affected (0.01 sec)
Mude para o novo banco de dados da
empresa
:
Confirme que você está conectado ao novo banco de dados verificando a seguinte saída:
Database changed
Crie uma tabela system_users
. A coluna user_id
serve como uma PRIMARY KEY
para identificar exclusivamente cada usuário. As colunas nome de usuário
e senha
são as credenciais de login que os usuários devem enviar para fazer login no aplicativo. As colunas first_name
e last_name
armazenam os nomes dos usuários:
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;
Certifique-se de ter criado a nova tabela verificando a seguinte saída:
Query OK, 0 rows affected (0.03 sec)
Preencha a tabela system_users
com dados de amostra. Use a função MD5(...)
integrada do MySQL para fazer o hash da senha para fins de segurança:
Verifique a saída abaixo:
Query OK, 1 row affected (0.00 sec)
Consulte a tabela system_users
para garantir que os dados estejam no lugar:
Verifique a seguinte saída:
+---------+------------+-----------+----------------------------------+| 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)
Saia do banco de dados MySQL:
Agora você configurou o banco de dados MySQL correto para sua aplicação. Na próxima etapa, você construirá um módulo Python comunicando-se com seu banco de dados de amostra.
Passo 3 — Criando um Módulo Central MySQL Gateway para Python
Ao codificar qualquer projeto Python, você deve criar um módulo separado para cada tarefa para promover a reutilização do código. Nesta etapa, você configurará um módulo central que permite conectar e consultar o banco de dados MySQL a partir de um script Python. Siga os passos abaixo:
Crie um diretório
projeto
. Este diretório separa seus arquivos de código-fonte Python do restante dos arquivos do sistema:
Mude para o novo diretório project
:
Use o editor de texto nano
para abrir um novo arquivo mysql_db.py
. Este arquivo hospeda o módulo Python que se comunica com o banco de dados MySQL:
nano mysql_db.py
Insira as seguintes informações no arquivo mysql_db.py
. Substitua example-mysql-password
pela senha correta do MySQL para a conta company_user
:
import mysql.connectorclass 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_condef 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]
Salve e feche o arquivo mysql_db.py
.
O arquivo do módulo mysql_db.py
possui uma classe (MysqlDb:
) com dois métodos: - db_con(self):
, conecta-se ao banco de dados de exemplo da company
que você criou anteriormente e retorna uma conexão MySQL reutilizável usando a instrução return mysql_con
. - query(self, username, password):
, um método que aceita um nome de usuário
e uma senha
e consulta os system_users
tabela para descobrir se há uma correspondência. A instrução condicional if row_count < 1: ... else: return result[1]
retorna o valor booleano False
se um usuário não existir na tabela ou na tabela do usuário senha (result[1]
) se o aplicativo encontrar uma correspondência.
Com o módulo MySQL pronto, siga a próxima etapa para configurar um módulo Redis semelhante que se comunique com o armazenamento de chave-valor do Redis.
Passo 4 — Criando um Módulo Redis Central para Python
Nesta etapa, você codificará um módulo que se conecta ao servidor Redis. Execute as seguintes etapas:
Abra um novo arquivo
redis_db.py
:nano redis_db.py
Insira as seguintes informações no arquivo
redis_db.py
. Substituaexample-redis-password
pela senha correta para o servidor Redis:import redisclass 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
Salve e feche o arquivo
redis_db.py
.
O arquivo acima possui uma classe (
RedisDb:
).Nesta classe, o método
db_con(self):
usa as credenciais fornecidas para se conectar ao servidor Redis e retorna uma conexão reutilizável usando a instruçãoreturn redis_con
.
Após configurar a classe Redis, crie o arquivo principal do seu projeto na próxima etapa.
Passo 5 — Criando o ponto de entrada do aplicativo
Cada aplicativo Python deve ter um ponto de entrada ou arquivo principal que é executado quando o aplicativo é executado. Neste arquivo, você criará um código que mostra a hora atual do servidor para usuários autenticados. Este arquivo usa os módulos MySQL e Redis personalizados que você criou para autenticar usuários. Siga as etapas abaixo para criar o arquivo:
Abra um novo arquivo
index.py
:nano index.py
Insira as seguintes informações no arquivo
index.py
:from encodings import utf_8import base64from hashlib import md5import jsonimport datetimeimport http.serverfrom http import HTTPStatusimport socketserverimport mysql_dbimport redis_dbclass 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.")
Salve e feche o arquivo
index.py
.
No arquivo
index.py
, a seçãoimport...
adiciona os seguintes módulos ao seu projeto:utf_8
,base64
,md5
ejson
, módulos de codificação e formatação de texto.
http.server
,HTTPStatus
esocketserver
, módulos de servidor web.datetime
, módulo de hora/data.mysql_db
eredis_db
, módulos personalizados que você criou anteriormente para acessar os servidores MySQL e Redis.
O HttpHandler(http.server.SimpleHTTPRequestHandler):
é uma classe manipuladora para o servidor HTTP. Na classe, o método do_GET(self):
atende as solicitações HTTP GET e exibe a data/hora do sistema para usuários autenticados.
Na lógica if ... : else: ...
, o script Python executa a instrução lógica if redis_client.exists(auth_user):
para verificar se as credenciais do usuário existem no servidor Redis. Se os detalhes do usuário existirem e a senha armazenada do Redis não corresponder à senha enviada pelo usuário, o aplicativo retornará o erro {"error": "Invalid username/password."}
.
Se os detalhes do usuário não existirem no servidor Redis, o aplicativo consultará o servidor de banco de dados MySQL usando a instrução mysql_resp=mysql_server.query(auth_user, auth_password)
. Caso a senha fornecida pelo usuário não corresponda ao valor armazenado no banco de dados, a aplicação retornará o erro {"error": "Invalid username/password."}
. Caso contrário, o aplicativo armazena em cache os detalhes do usuário no servidor Redis usando a instrução redis_client.set(auth_user, mysql_resp)
.
Em todos os casos em que as credenciais do usuário correspondem aos detalhes do Redis/MySQL, o aplicativo exibe a data/hora atual do sistema usando a instrução
{"time": current_time, ...}
. A entradaauthorized by
na saída permite que você veja o servidor de banco de dados que autentica os usuários no aplicativo.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)
Agora você configurou o arquivo principal do aplicativo. Na próxima etapa, você testará o aplicativo.
Passo 6 — Testando o Aplicativo
Nesta etapa, você executará seu aplicativo para verificar se o mecanismo de cache do Redis funciona. Execute os comandos abaixo para testar o aplicativo:
Use o seguinte comando
python3
para executar o aplicativo:python3 index.py
Certifique-se de que o servidor web personalizado do aplicativo esteja em execução:
Web server is running on port 8080...
Estabeleça outra conexão
SSH
com seu servidor em uma nova janela de terminal e execute os seguintes comandoscurl
para enviar quatro solicitações GET usandojohn_doe's
credenciais. Acrescente[1-4]
no final da URLhttp://localhost:8080/
para enviar as quatro solicitações em um único comando:curl -X GET -u john_doe:password_1 http://localhost:8080/[1-4]
Verifique as seguintes saídas. O servidor MySQL atende apenas a primeira solicitação de autenticação. Em seguida, o banco de dados Redis atende às próximas três solicitações.
[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"}
A lógica do seu aplicativo agora está funcionando conforme o esperado.
Conclusão
Neste guia, você construiu um aplicativo Python que usa o servidor Redis para armazenar em cache as credenciais de login dos usuários. Redis é um servidor de banco de dados altamente disponível e escalonável que pode realizar milhares de transações por segundo. Com o mecanismo de cache Redis em seu aplicativo, você pode reduzir bastante o tráfego em seu servidor de banco de dados back-end. Para saber mais sobre aplicativos Redis, consulte nossos tutoriais Redis.