microservices

Table of Contents


Como visto no Step4, foi iniciado os primeiros passos para interagir com a API gateway, e foi criado as configurações para a inicialização do container, quando for subir para a OCI.

Nesse passo, mostrarei como configurar a conexão com os servidores do Rabbit, Redis e Postgres, e como planejei a arquitetura de pastas e módulos para ter uma aplicação organizada.

Dentro da pasta API, devemos criar uma subpasta chamada config com os seguintes arquivos:

MS-application
└───API
    │   docker-compose-api.yml
    │   Dockerfile
    │   requirements.txt
    │   server.py
    │
    ├───config
    │       database_connection.py
    │       rabbitmq_connection.py
    │       redis_connection.py
    │       __init__.py

Postgres

Antes de continuar, deve-se ter certeza de que executou todas as instalações das bibliotecas, contidas no arquivo requeriments.txt.

Para começarmos, devemos importar os módulos _psycopg2_e os, que iremos utilizar para a conexão.

import psycopg2, os

Como teremos que apontar o host do servidor Postgres para nossa aplicação, na criação do Dockerfile foi criado uma variável de ambiente chamada HOST_DATABASE, contendo o ip privado da instância da OCI. Porém, essa variável de ambiente só será utilizada em Produção. Sendo assim, criei uma variável dentro do arquivo database_connection.py contendo o ip público, somente para fins de desenvolvimento em Homologação, utilizando os serviços que já estão rodando.

Já que nos conectaremos a um banco de dados, essa deve ser uma das primeiras coisas que deve acontecer quando a API for inicializada. Para isso, criaremos uma classe chamada ConnectionDatabase(), onde em seu __init__ iremos fazer esse processo de conexão.

Dentro da função connect da biblioteca psycopg2, vamos colocar os parâmetros host, que serão o ip da instancia OCI, port contendo a numeração padrão da porta postgres (já foi liberada a porta no OCI para a conexão externa), batabase, contendo o nome do banco, user e password, contendo usuário e senha que foi colocado na imagem que subimos do postgres.

Após o apontamento dos parâmetros obrigatórios para a conexão, criaremos um atributo chamado cursor, que será o responsável por executar qualquer interação com o banco de dados. Logo abaixo há um método create_tables, que criaremos logo em seguida. Para finalizar, tudo isso está dentro de um try-except, para caso haja alguma falha na conexão, seja lançado um except com o erro.

# HOST_DATABSE = os.environ['HOST_DATABASE']
HOST_DATABSE = '144.22.193.219'

class ConnectionDatabase():

    def __init__(self):
        try:
            self.connection  = psycopg2.connect(
                host=HOST_DATABSE,
                port=5432,
                database="baseapplication",
                user="postgres",
                password="postgres")
            self.cursor = self.connection.cursor()
            # start tables
            self.create_tables()
            print('[✓] Connected to Postgres')
        except Exception as error:
            print(f'[X] CONNECTING POSTGRES ERROR: {error}')

Create_tables

Antes de começar a fazer a função que criará as tabelas, vamos analisar o que precisará ser feito com o modelo ER abaixo:

img27

Iremos criar uma estrutura simples. Serão duas tabelas, users e orders. Nelas conterão seus atributos, com seus determinados tipos. Note que na tabela orders, iremos herdar um atributo da tabela user, que será o user_id, onde na tabela user é a PrimaryKey. Já na tabela orders, o atributo herdado é o user_id, que será tratado como uma chave estrangeira (ForeignKey).

Em relação a cardinalidade vamos ter 1 usuário pode ter vários orders, e 1 order pode ter somente um usuário, representados pelos símbolos abaixo.

img28

Após essa análise, devemos criar o método create­_table, e criar as tabelas. Para executar comandos SQL através da biblioteca, basta escrever em uma string o comando SQL que deseja executar, e passar como parâmetro para função execute. Visto isso, primeiramente criaremos um select da versão do Postgres, para que toda a vez que seja iniciado a conexão, seja mostrado a versão. Logo após, devemos criar um atributo contendo toda a query de criação das duas tabelas, baseada do modelo ER visto anteriormente. Sendo assim, como método já foi chamado no __init__, será criado as tabelas ao instanciar a classe ConnectionDatabase.

def create_tables(self):
        self.cursor.execute("SELECT version();")
        record = self.cursor.fetchone()
        print("[✓] You are connected to - ", record)

        create_table_query = '''
            CREATE TABLE IF NOT EXISTS users (
                user_id SERIAL NOT NULL,
                nick_name varchar(50) UNIQUE NOT NULL,
                password varchar(256) NOT NULL,
                full_name varchar(50) NOT NULL,
                cpf varchar(11) NOT NULL,
                email varchar(50) NOT NULL,
                phone_number varchar(50) NOT NULL,
                created_at TIMESTAMP,
                updated_at TIMESTAMP,
                PRIMARY KEY (user_id)
            );

            CREATE TABLE IF NOT EXISTS orders (
                order_id SERIAL NOT NULL,
                user_id integer NOT NULL,
                item_description varchar(256) NOT NULL,
                item_quantity integer NOT NULL,
                item_price integer NOT NULL,
                total_value integer NOT NULL,
                created_at TIMESTAMP,
                updated_at TIMESTAMP,
                PRIMARY KEY (order_id),
                FOREIGN KEY(user_id) REFERENCES users(user_id)
            );'''
        
        self.cursor.execute(create_table_query)
        self.connection.commit()
        print('[✓] Created tables on DataBase')

Rabbit MQ

O Rabbit é um message borker open source fácil e leve de ser implementado, tanto local, quanto em nuvem. O rabbit suporta vários tipos de protocolo de mensagens, para haver facilidade na comunicação entre aplicações. Existem muitos outros recursos, porém, não irei detalhar eles nesse projeto.

Nesse momento, faremos somente a conexão com o servidor Rabbit, onde usaremos uma variável de ambiente como no postgres, no entanto ela será a HOST, contida no Dockerfile. Da mesma forma que na conexão do postgres, criei uma variável estática contendo o ip público, somente para desenvolver a aplicação local, utilizando o servidor que já estão rodando os serviços.

Começamos com a importação das bibliotecas necessárias e o apontamento da variável com o IP Público da instancia OCI.

import pika, os

import pika, os

# HOST_RABBIT = os.environ['HOST']
HOST_DATABSE = '144.22.193.219'

Agora iremos criar uma classe chamada RabbitConnection(), e faremos a conexão no método __init__. Utilizando o função de conexão da biblioteca pika chamada BlockingConnection(), passaremos como parâmetros, através do metodo ConnectionParameters() o host, contida na variável HOST_RABBIT, e a porta padrão 5672. Em seguida, será criado um atributo chamado channel, que será responsável por executar toda e qualquer interação com o servidor rabbit, tudo isso foi criado em um try-catch, para caso haja alguma falha na conexão, seja lançado um except com o erro

class RabbitConnection():

    def __init__(self) :
        try:
            self.connection = pika.BlockingConnection(
                pika.ConnectionParameters(host=HOST_RABBIT, port=5672))
            self.channel = self.connection.channel()
            print('[✓] Connected to RabbitMQ server')
            
        except Exception as error:
            print(f'[X] CONNECTING RABBIT MQ ERROR: {error}')

Redis

Redis é um armazenamento de estrutura de dados na memória de código aberto (licenciado BSD), usado como banco de dados, cache, agente de mensagens e mecanismo de streaming. Um dos serviços que se destaca é o cache, que iremos utilizar nessa aplicação.

Para criar a conexão com o servidor Redis, utilizaremos a variável de ambiente CACHE_REDIS_HOST, porém, para a fim de desenvolvimento, criarei uma variável local contendo o ip público da instancia.

Nessa configuração, iremos fazer algo um pouco diferente. Como o Redis e Flask conseguem trabalhar juntos ativamente, a configuração para a conexão ao servidor, será feita através do arquivo principal server.py. Contudo, será demonstrado como fazer isso no próximo passo.

No momento a configuração será a seguinte: Importar a biblioteca os, criar uma classe chamada BaseConfig(), e dentro dela, apontar as variáveis de ambiente contidas no Dockerfile, veja:

import os

class BaseConfig(object):
    CACHE_TYPE = os.environ['CACHE_TYPE']
    CACHE_REDIS_HOST = os.environ['CACHE_REDIS_HOST']
    CACHE_REDIS_PORT = os.environ['CACHE_REDIS_PORT']
    CACHE_REDIS_DB = os.environ['CACHE_REDIS_DB']

Como testarei localmente para homologação, criarei a mesma classe, porém, com atributos com valor estático do servidor.

class BaseConfig(object):
    CACHE_TYPE = 'redis' 
    CACHE_REDIS_HOST = '144.22.193.219'
    CACHE_REDIS_PORT = '6379'
    CACHE_REDIS_DB = 0

Resumo

Nesse Step, criamos as conexões com os servidores Rabbit, Redis e Postgres que já estavam rodando a instancia da OCI. Criamos a conexão com o Postgres. Foi explanado o modelo de entidade relacional que posteriormente foi desenvolvido, criando as devidas tabelas planejadas. Vimos um breve resumo do que é o rabbit e criamos a conexão com o servidor que está rodando na OCI. E por fim, apontado a conexão com o Redis, porém não utilizaremos nesse momento.