Serviço de ação do usuário: do monólito à nuvem

(Souhaib Guitouni) (23 de junho de 2020)

Introdução

Historicamente, o backend do BlaBlaCar era um grande monólito PHP. Isso nos permitiu iterar e construir recursos rapidamente, mas, a longo prazo, não era fácil apreender, manter e evoluir. Nos últimos anos, temos quebrado esse velho monólito e nos movido em direção a uma Arquitetura Orientada a Serviços.

Este artigo é uma das histórias de como estamos gerenciando essa transição, com alguns detalhes sobre o ferramentas que nos ajudaram ao longo do caminho. Ele cobrirá as escolhas técnicas da pilha, a arquitetura geral e feedback sobre as ferramentas que usamos.

I – Os porquês

Conforme a atividade do BlaBlaCar se expande, ter uma arquitetura monolítica se torna cada vez mais limitando, principalmente porque:

  • Enquanto vivem, os monólitos caem em acoplamento;
  • Ter equipes trabalhando em pendências separadas é difícil de organizar na mesma base de código;
  • Monólitos são difíceis de apreender para iniciantes;
  • Por tudo isso, qualquer nova funcionalidade torna-se extremamente cara.

É por isso que, nos últimos anos, temos redesenhado nossa plataforma para uma arquitetura SOA, procurando por:

  • Servidores com escopo funcional: um serviço gerencia uma única funcionalidade e é totalmente responsável por como ela a implementa.
  • Serviços fracamente acoplados que podem evoluir independentemente. As equipes podem trabalhar e implantar em seu próprio ritmo.
  • Mais propriedade de código, portanto, os serviços têm um escopo definido e mais reduzido, permitindo que as equipes os mantenham e operem com mais facilidade.

II – Escopo e histórico das ações do usuário

O mais recente na fila para se tornar um serviço independente foram as ações do usuário. Eles ajudam nossa Equipe de Relações com a Comunidade a diagnosticar e resolver problemas de usuários, como reembolsar e detectar anomalias ou rastrear comportamento fraudulento.

A quantidade de ações do usuário, como você pode esperar, é colossal. Historicamente, como uma tabela SQL, esta parte do código apresentava problemas de desempenho. Sempre encontramos soluções, mas com nossa expansão, os problemas foram ficando cada vez maiores:

  • Uma primeira solução era criar índices para tornar as consultas mais rápidas;
  • Outra era fragmentar o banco de dados em várias tabelas.

Mesmo assim, essas soluções atingiram seu limite e com o tempo, começamos a ter problemas de desempenho.

III – Armazenamento de dados

1 – O caminho para o NoSQL

A escolha do armazenamento de dados foi baseada principalmente na técnica requisitos:

  • Existem grandes quantidades de dados
  • Grande volume de inserções
  • Várias leituras por segundo, com a necessidade de novos dados e desempenho
  • Possibilidade de filtrar em um tempo razoável

Obviamente estávamos procurando por um banco de dados NoSQL, já que a distribuição nos permitiria armazenar e manipular grandes quantidades de dados e escala quando necessário.

2 – Serviço gerenciado

Também queríamos ir com um serviço gerenciado para reduzir o custo da manutenção. Bancos de dados distribuídos como Hbase e Cassandra são famosos pelas dificuldades que geram para as equipes de gerenciamento de banco de dados. Mais importante, para a equipe de desenvolvimento de aplicativos, queríamos nos concentrar na produção da lógica de manipulação das ações do usuário, em vez de gastar nosso tempo corrigindo problemas com a infraestrutura do banco de dados. Foi também a oportunidade de testar um novo produto, já que o escopo de aplicação não é muito amplo. Queríamos experimentar algo novo!

3 – Escolha final

A escolha caiu no Bigtable pelos seguintes motivos:

  • Volume: foi projetado para lidar com grandes quantidades de dados, ao contrário da maioria dos bancos de dados SQL, uma instância pode lidar com 10.000 inserções por segundo, então escolhemos a configuração mínima com apenas três nós.
  • Escalabilidade: é escalonável e fácil de replicar, você também pode gerenciar versões de dados.
  • Custo de gerenciamento: é um serviço gerenciado.
  • Desempenho: é um serviço GCP, o que nos permitirá ter um excelente desempenho, pois estará na mesma infraestrutura do serviço implantado no GKE.

III – Saltar de um trem em movimento para outro

O difícil sobre mudar de um monólito para um serviço é fornecer continuidade de serviço de um ponto de vista externo e não ter perda de dados.

Para fornecer isso, decidimos executar o novo serviço, manter o sistema antigo funcionando e escrever as novas ações via REST em ambos. A próxima etapa será mover os dados históricos do monólito para o novo serviço. Esta operação pode criar duplos, portanto, o carregamento de dados inicial deve lidar com a desduplicação. Uma terceira etapa será ler o novo serviço em vez do sistema antigo. Uma última etapa seria desligar a parte do monolith e o banco de dados SQL legado assim que tudo estiver funcionando no serviço.

IV – Movendo dados

1 – Do MySQL ao Bigtable

Agora que decidimos qual será o nosso armazenamento de dados de destino e como será implantado, começamos a pensar sobre as maneiras de mover dados do monólito para o novo banco de dados. Tal operação é crítica e o trabalho de migração deve ser:

  • Simples e fácil de desenvolver: a complexidade sendo os conectores de banco de dados
  • Alto desempenho: pois terá uma leitura enorme quantidades de dados, queremos ter o resultado o mais rápido possível
  • Confiável: não queríamos perda de dados
  • Fácil de reiniciar: caso as coisas dêem errado, queríamos algo que pudéssemos simplesmente reinicie sem muitas ações manuais, com scripts de shell em todos os lugares e um procedimento complexo
  • Permitindo transformações: precisávamos juntar dados de vários bancos de dados MySQL e tabelas para obter os Bigtable finais, isso poderia ser difícil se com script manualmente.

2 – Apache Beam e Dataflow

Escolhemos usar o Dataflow, um serviço gerenciado distribuído fornecido pelo Google Cloud, que permite que as pessoas movam dados em Lote e em modos de streaming.

O Apache Beam, por outro lado, é um Framework usado para codificar scripts distribuídos . Você pode então executá-lo em muitos mecanismos distribuídos, como Dataflow, Spark, Flink … Em outras palavras, é uma camada de abstração para definir suas transformações que podem ser entendidas por vários mecanismos.

Outra coisa legal sobre o Apache Beam é que tem conectores prontos para uso para várias fontes de dados, entre elas MySQL e Bigtable. Então criamos as transformações de dados, lendo as tabelas legadas do Mysql, juntando-as e colocando o resultado no Bigtable.

2 – Como foi?

Fizemos algumas observações durante a execução da migração:

  • Foi uma tarefa exaustiva para o MySQL: evitar problemas de desempenho, você deve, por qualquer meio, evitar executá-lo em um banco de dados crítico. Para evitar esse problema, o executamos em um banco de dados de réplica feito para essas tarefas. Acompanhamos a execução em nosso serviço de monitoramento (Prometheus / Grafana) que mostrou que as máquinas paralelas do Dataflow esgotam o MySQL.
  • Também não foi fácil para o Bigtable: tínhamos uma pequena instância de 3 máquinas, capaz de fornecer 30.000 inserções por segundo. Se você executar vários jobs do Dataflow, vai muito além disso. Portanto, pense com sabedoria em quantas máquinas você deseja ter em paralelo, porque ter o número máximo fará com que você perca dinheiro.
  • O conector MySQL Beam não transmite dados: o conector MySQL executa uma instrução SQL e espera para carregar a resposta inteira para continuar. Isso causará problemas com a RAM se você carregar muitos dados. Portanto, você precisa testar e definir os intervalos de usuários para evitar terminar com uma exceção de memória.

V – O serviço de back-end

Vários padrões de arquitetura eram possíveis, nós queria ir com uma arquitetura que pudesse evoluir no futuro, e que pudéssemos construir passo a passo, com tráfego ativo desde a primeira etapa.

Decidimos criar um serviço com Spring WebFlux, a estrutura reativa , para ter solicitações sem bloqueio e melhor desempenho com terminais funcionais. Usamos o Reactor para a melhor integração com Spring.

1 – Teste de integração com Bigtable

Os testes de integração chamam a API com vários payloads / cabeçalhos e verificam se a resposta é a esperada. Para fazer isso, precisávamos de uma instância Bigtable por trás de nosso serviço, pois queríamos testes da vida real.

Essa instância precisa ser limpa em cada execução para começar do mesmo estado todas as vezes.

A solução foi usar o emulador Bigtable presente no SDK do cliente Bigtable. O SDK é muito rico e tem mais recursos, como gerenciamento e monitoramento de administração.

2 – Implantação e infraestrutura

Nossa infraestrutura GCP é baseada no Google Kubernetes e no Istio. Gerenciamos tudo com Helm e Flux. Outros artigos estão chegando em nosso blog para entrar em mais detalhes sobre nossa infraestrutura!

Claro, nosso serviço tinha fins clássicos de monitoramento, registro e alerta, sem eles não poderíamos executá-lo. , baseado em Prometheus / Grafana e ELK.

Considerações finais

Para gerenciar a grande quantidade de dados, fomos para o Bigtable graças à sua capacidade de escalonamento.Tínhamos vários desafios, como definir o esquema de dados com o padrão de chave Bigtable, mover grandes quantidades de dados de um banco de dados SQL local para um NoSQL gerenciado, testar e monitorar tudo.

No início, implantamos o serviço em nosso datacenter local e, ao mudar para o GCP, dividimos a latência por três. Nossas equipes de relações com a comunidade agora têm tempos de resposta mais rápidos e estão entusiasmadas.