Pesquisa de site

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:

  1. 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
  1. Instale o driver MySQL para Python:
pip install mysql-connector-python
  1. 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:

  1. Faça login no servidor de banco de dados MySQL como usuário root:

    sudo mysql -u root -p
  2. Digite a senha root do seu servidor MySQL quando solicitado e pressione ENTER para continuar. Em seguida, execute o comando a seguir para criar um banco de dados company de amostra e uma conta company_user. Substitua example-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;

  1. 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)
  2. Mude para o novo banco de dados da empresa:

    1. Confirme que você está conectado ao novo banco de dados verificando a seguinte saída:

      Database changed
    2. 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;
    3. Certifique-se de ter criado a nova tabela verificando a seguinte saída:

      Query OK, 0 rows affected (0.03 sec)
    4. 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:

      1. INSERT INTO system_users (nome de usuário, nome, sobrenome, senha) VALUES ('mary_henry', 'MARY', 'HENRY', MD5('password_2'));
      2. INSERT INTO system_users (nome de usuário, nome, sobrenome, senha) VALUES ('peter_jade', 'PETER', 'JADE', MD5('password_3'));
    5. Verifique a saída abaixo:

      Query OK, 1 row affected (0.00 sec)
    6. Consulte a tabela system_users para garantir que os dados estejam no lugar:

      1. ID do usuário,
      2. primeiro nome,
      3. sobrenome,
      4. senha
      5. DE usuários_do_sistema;
    7. 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)
    8. 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:

      1. Crie um diretório projeto. Este diretório separa seus arquivos de código-fonte Python do restante dos arquivos do sistema:

        1. Mude para o novo diretório project:

          1. 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
          2. 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]
          3. 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:

          1. Abra um novo arquivo redis_db.py:

            nano redis_db.py
          2. Insira as seguintes informações no arquivo redis_db.py. Substitua example-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
          3. 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ção return 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:

          1. Abra um novo arquivo index.py:

            nano index.py
          2. 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.")
          3. Salve e feche o arquivo index.py.

          • No arquivo index.py, a seção import... adiciona os seguintes módulos ao seu projeto:

            • utf_8, base64, md5 e json, módulos de codificação e formatação de texto.

            • http.server, HTTPStatus e socketserver, módulos de servidor web.

            • datetime, módulo de hora/data.

            • mysql_db e redis_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 entrada authorized 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:

          1. Use o seguinte comando python3 para executar o aplicativo:

            python3 index.py
          2. Certifique-se de que o servidor web personalizado do aplicativo esteja em execução:

            Web server is running on port 8080...
          3. Estabeleça outra conexão SSH com seu servidor em uma nova janela de terminal e execute os seguintes comandos curl para enviar quatro solicitações GET usando john_doe's credenciais. Acrescente [1-4] no final da URL http://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]
          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.