Tutorial de microsserviços do Citus

Neste tutorial, você usará o Citus como back-end de armazenamento para vários microsserviços. O tutorial demonstra uma configuração de exemplo e uma operação básica desse cluster.

Observação

Este tutorial pressupõe que você tenha o Citus instalado e em execução. Se você não tiver o Citus em execução, poderá configurar o Citus localmente usando uma das opções de Introdução.

Esquemas distribuídos

Esquemas distribuídos são relocáveis em um cluster Citus. O sistema pode rebalanceá-los como uma unidade inteira entre os nós disponíveis, permitindo compartilhar recursos com eficiência sem alocação manual.

Por design, os microsserviços possuem sua camada de armazenamento. Não presumimos o tipo de tabelas e dados que eles criam e armazenam. Fornecemos um esquema para cada serviço e presumimos que eles usam uma FUNÇÃO diferente para se conectar ao banco de dados. Quando um usuário se conecta, seu nome de função é colocado no início do search_path, portanto, se a função corresponder ao nome do esquema, você não precisará de nenhuma alteração de aplicativo para definir o search_path correto.

Usamos três serviços em nosso exemplo:

  • serviço de usuário
  • serviço de tempo
  • serviço ping

Para começar, conecte-se ao coordenador do Citus usando psql.

Se você estiver usando o PostgreSQL nativo, conforme instalado em Introdução, o nó coordenador estará em execução na porta 9700.

psql -p 9700

Se você usar o Docker, poderá se conectar executando o psql com o comando docker exec:

docker exec -it citus psql -U postgres

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

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

Você pode distribuir um esquema no Citus de duas maneiras:

Chamando manualmente a função citus_schema_distribute(schema_name):

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 os esquemas regulares existentes em esquemas distribuídos.

Observação

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

Como alternativa, habilite a variável de configuração citus.enable_schema_based_sharding.

SET citus.enable_schema_based_sharding TO ON;

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

Você pode alterar a variável para a sessão atual ou permanentemente em postgresql.conf. Quando você define o parâmetro como ON, todos os esquemas criados são distribuídos por padrão.

Você pode listar os esquemas distribuídos no momento:

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)

Criando tabelas

Você precisa se conectar ao coordenador do Citus para cada microsserviço. Use o comando c para trocar o usuário em uma instância psql existente.

\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

Para este tutorial, usamos os serviços que você pode obter clonando este repositório público:

git clone https://github.com/citusdata/citus-example-microservices.git O repositório contém o ping, o tempo e o serviço do usuário. Todos eles têm um app.py que nós executamos.


$ 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, edite os arquivos de usuário/app.py, ping/app.py e hora/app.py para fornecer a configuração de conexão para o cluster Citus:


# Database configuration

db_config = {
    'host': 'localhost',
    'database': 'citus',
    'user': 'ping_service',
    'port': 9700
}

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

Executando os serviços

Acesse cada diretório de aplicativo e execute os aplicativos em seu próprio ambiente Python.


cd user
pipenv install
pipenv shell
python app.py

Repita as etapas anteriores para os serviços de tempo e ping. Quando terminar, 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

Listar os usuários criados:


curl http://localhost:5000/users

Obtenha a hora atual:


curl http://localhost:5001/current_time

Execute o ping em example.com:


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

Explorando o banco de dados

Depois de chamar algumas funções de API, o sistema armazena dados. Você pode verificar se a tabela citus_schemas reflete o que você espera:


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)

Ao criar os esquemas, você não especifica quais computadores usar. O Citus escolhe automaticamente os computadores. 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 |     9701 | time_service.query_details | 32 kB
localhost |     9702 | user_service.users         | 112 kB
localhost |     9702 | ping_service.ping_results | 32 kB

Podemos ver que o serviço de hora foi alocado no nó localhost:9701, enquanto o serviço de usuário e o de ping compartilham espaço no segundo nó de trabalho localhost:9702. Este é um exemplo simples e os tamanhos de dados aqui são ignoráveis, mas vamos supor que estamos incomodados com a utilização desigual do espaço de armazenamento entre os nós. Faria mais sentido fazer com que os dois serviços menores de ping e tempo residam em um computador, enquanto o serviço grande de usuário reside sozinho.

Você pode corrigir esse problema solicitando ao Citus para reequilibrar o cluster pelo tamanho do 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 a operação for concluída, você poderá verificar a aparência do 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 |     9701 | time_service.query_details | 32 kB
localhost |     9701 | ping_service.ping_results | 32 kB
localhost |     9702 | user_service.users         | 112 kB
(3 rows)

De acordo com nossas expectativas, os esquemas são movidos e temos um cluster mais equilibrado. Essa operação é transparente para os aplicativos. Nem precisamos reiniciá-los e eles continuam atendendo consultas.

Ao concluir este tutorial, você chegou ao final do uso do Citus como armazenamento para microsserviços.