Tutorial: Projeto de microsserviços com clusters elásticos

Neste tutorial, utilizas o Azure Database para PostgreSQL como backend de armazenamento para múltiplos microserviços. O tutorial demonstra um exemplo de configuração e operação básica de tal cluster. Saiba como:

  • Pré-requisitos
  • Criar funções para seus microsserviços
  • Use o utilitário psql para criar funções e esquemas distribuídos
  • Criar tabelas para os serviços de exemplo
  • Configurar serviços
  • Executar serviços
  • Explore a base de dados

Pré-requisitos

Crie um cluster elástico de uma das seguintes maneiras:

Criar funções para seus microsserviços

Pode colocar esquemas distribuídos dinamicamente dentro de um cluster elástico. O sistema pode reequilibrá-los como uma unidade inteira entre os nós disponíveis, por isso obtém maior eficiência nos recursos do cluster sem necessidade de alocação manual.

Quando aplica a fragmentação de esquema a um padrão de design de microserviços, cria um esquema de base de dados para cada microserviço correspondente. Além disso, use um ROLE distinto para cada microserviço ao ligar-se à base de dados. Quando cada utilizador se liga, o nome da sua função aparece no início da search_path. Se o nome da função coincidir com o nome do esquema, não precisas de alterações adicionais na aplicação para definir a search_path correta.

Neste exemplo, use três microsserviços:

  • utilizador
  • time
  • enviar ping

Crie as funções de base de dados para cada serviço:

CREATE USER user_service;
CREATE USER time_service;
CREATE USER ping_service;

Use o utilitário psql para criar esquemas distribuídos

Depois de se ligar ao cluster elástico usando psql, pode completar algumas tarefas básicas.

Pode distribuir um esquema de duas formas:

  • Manualmente ao chamar a citus_schema_distribute(schema_name) função:
CREATE SCHEMA AUTHORIZATION user_service;
CREATE SCHEMA AUTHORIZATION time_service;
CREATE SCHEMA AUTHORIZATION ping_service;

SELECT citus_schema_distribute('user_service');
SELECT citus_schema_distribute('time_service');
SELECT citus_schema_distribute('ping_service');

Esse método também permite converter esquemas regulares existentes em esquemas distribuídos.

Observação

Você só pode distribuir esquemas que não contenham tabelas distribuídas e de referência.

  • Ativando a citus.enable_schema_based_sharding variável de configuração. Podes alterar a variável para a sessão atual ou permanentemente a partir dos parâmetros do nó coordenador. Quando defines o parâmetro para ON, todos os esquemas criados são distribuídos por padrão.
SET citus.enable_schema_based_sharding TO ON;

CREATE SCHEMA AUTHORIZATION user_service;
CREATE SCHEMA AUTHORIZATION time_service;
CREATE SCHEMA AUTHORIZATION ping_service;

Execute o seguinte comando para listar os esquemas atualmente distribuídos:

SELECT * FROM citus_schemas;
 schema_name | colocation_id | schema_size | schema_owner
-------------+---------------+-------------+--------------
 user_service |             5 | 0 bytes     | user_service
 time_service |             6 | 0 bytes     | time_service
 ping_service |             7 | 0 bytes     | ping_service
(3 rows)

Criar tabelas para os serviços de exemplo

Agora podes ligar-te ao cluster elástico de todos os microserviços. No exemplo seguinte, a base de dados de clusters elásticos chama-se Citus. A partir da sessão psql, podes usar o comando \c para trocar para outro utilizador.

\c citus user_service
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL
);
\c citus time_service
CREATE TABLE query_details (
    id SERIAL PRIMARY KEY,
    ip_address INET NOT NULL,
    query_time TIMESTAMP NOT NULL
);
\c citus ping_service
CREATE TABLE ping_results (
    id SERIAL PRIMARY KEY,
    host VARCHAR(255) NOT NULL,
    result TEXT NOT NULL
);

Configurar serviços

Neste tutorial, usamos um conjunto simples de serviços. Você pode obtê-los clonando este repositório público:

git clone https://github.com/citusdata/citus-example-microservices.git
$ tree
.
├── LICENSE
├── README.md
├── ping
│   ├── app.py
│   ├── ping.sql
│   └── requirements.txt
├── time
│   ├── app.py
│   ├── requirements.txt
│   └── time.sql
└── user
    ├── app.py
    ├── requirements.txt
    └── user.sql

Antes de executar os serviços, no entanto, edite user/app.py, ping/app.pye time/app.py os arquivos que fornecem a configuração de conexão para seu cluster elástico:

# Database configuration
db_config = {
    'host': 'EXAMPLE.postgres.database.azure.com',
    'database': 'postgres',
    'password': 'SECRET',
    'user': 'ping_service',
    'port': 5432
}

Depois de fazer as alterações, salve todos os arquivos modificados e passe para a próxima etapa de execução dos serviços.

Executar serviços

Mude para cada diretório de aplicativos e execute-os em seu próprio python env.

cd user
pipenv install
pipenv shell
python app.py

Repita os comandos para o serviço de tempo e ping, após o qual você pode usar a API.

Crie alguns usuários:

curl -X POST -H "Content-Type: application/json" -d '[
  {"name": "John Doe", "email": "john@example.com"},
  {"name": "Jane Smith", "email": "jane@example.com"},
  {"name": "Mike Johnson", "email": "mike@example.com"},
  {"name": "Emily Davis", "email": "emily@example.com"},
  {"name": "David Wilson", "email": "david@example.com"},
  {"name": "Sarah Thompson", "email": "sarah@example.com"},
  {"name": "Alex Miller", "email": "alex@example.com"},
  {"name": "Olivia Anderson", "email": "olivia@example.com"},
  {"name": "Daniel Martin", "email": "daniel@example.com"},
  {"name": "Sophia White", "email": "sophia@example.com"}
]' http://localhost:5000/users

Liste os usuários criados:

curl http://localhost:5000/users

Obter hora atual:

Get current time:

Corra o ping para example.com:

curl -X POST -H "Content-Type: application/json" -d '{"host": "example.com"}' http://localhost:5002/ping

Explore a base de dados

Agora que você chamou algumas funções da API, os dados são armazenados e você pode verificar se citus_schemas reflete o que é esperado:

SELECT * FROM citus_schemas;
 schema_name | colocation_id | schema_size | schema_owner
-------------+---------------+-------------+--------------
 user_service |             1 | 112 kB      | user_service
 time_service |             2 | 32 kB       | time_service
 ping_service |             3 | 32 kB       | ping_service
(3 rows)

Quando criaste os esquemas, não indicaste em que máquinas criar os esquemas. Foi feito automaticamente. Você pode ver onde cada esquema reside com a seguinte consulta:

  SELECT nodename,nodeport, table_name, pg_size_pretty(sum(shard_size))
  FROM citus_shards
  GROUP BY nodename,nodeport, table_name;
nodename  | nodeport |         table_name         | pg_size_pretty
-----------+----------+---------------------------+----------------
 localhost |     7001 | time_service.query_details | 32 kB
 localhost |     7002 | user_service.users         | 112 kB
 localhost |     7002 | ping_service.ping_results  | 32 kB

Para brevidade da saída de exemplo nesta página, em vez de usar nodename como um endereço IP, nós o substituímos por localhost. Suponha que localhost:7001 é o nó um e localhost:7002 é o nó dois.

Pode ver que o serviço de tempo foi colocado no nó localhost:7001, enquanto o utilizador e o serviço de ping partilham o espaço no segundo nó localhost:7002. As aplicações de exemplo são simples e os tamanhos dos dados aqui são insignificantes, mas vamos assumir que é afetado pela utilização desigual do espaço de armazenamento entre os nós. Faria mais sentido que os dois serviços de tempo e ping mais pequenos residissem num nó enquanto o serviço de utilizador grande ficasse num nó próprio.

Você pode facilmente reequilibrar o cluster por tamanho de disco:

SELECT citus_rebalance_start();
NOTICE:  Scheduled 1 moves as job 1
DETAIL:  Rebalance scheduled as background job
HINT:  To monitor progress, run: SELECT * FROM citus_rebalance_status();
 citus_rebalance_start
-----------------------
                     1
(1 row)

Quando terminar, você pode verificar como fica o nosso novo layout:

  SELECT nodename,nodeport, table_name, pg_size_pretty(sum(shard_size))
  FROM citus_shards
  GROUP BY nodename,nodeport, table_name;
 nodename  | nodeport |         table_name        | pg_size_pretty
-----------+----------+---------------------------+----------------
 localhost |     7001 | time_service.query_details | 32 kB
 localhost |     7001 | ping_service.ping_results  | 32 kB
 localhost |     7002 | user_service.users         | 112 kB
(3 rows)

De acordo com as expectativas, os esquemas são movidos e temos um cluster mais equilibrado. Esta operação é transparente para as aplicações. Nem sequer precisa de os reiniciar, eles continuam a responder às consultas.

Próximo passo