Microsserviços: como cumprir o contrato

A transição para a arquitetura de microsserviço requer uma revisão da abordagem de desenvolvimento, teste, manutenção, design - em outras palavras, para todos os aspectos do ciclo de vida dos componentes de software. Neste post, falaremos sobre as práticas que a equipe de arquitetos da Acronis adotou para avançar para as melhores APIs de componentes. A história incluirá tanto a declaração do problema quanto a análise de suas soluções. Talvez este post pareça "capitão" para alguns, não ficará claro para alguém por que eles perderam a super-solução X, mas esperamos que seja interessante e útil para você. Convidamos os construtores de microsserviços a ler e deixar seus comentários em cat.

imagem

Se você se inscreveu em nosso blog, já leu sobre contratos de microsserviço. Falamos sobre eles em posts sobre a escolha do Swagger ou RAML , bem como verificações estáticas que podem ser realizadas com base em anotações criadas anteriormente. O motivo da publicação de hoje foi um relatório na conferência HighLoad . Geralmente, precisamos conversar sobre o caminho que seguimos para formalizar o relacionamento entre microsserviços. E hoje quero compartilhar nossas conclusões com Habr e também verificar se outros arquitetos concordam conosco.

Os microsserviços são os "tijolos" dos quais os desenvolvedores criam aplicativos modernos. Cada um desses serviços interage com o mundo externo por meio de uma API. Geralmente, os microsserviços são desenvolvidos por equipes separadas, às vezes geograficamente dispersas; portanto, para um trabalho eficaz, é necessário manter a consistência e a integridade de suas interfaces públicas. Em grandes empresas com centenas de serviços, é necessário ter uma anotação de cada componente: formalizar os dados de entrada e descrever detalhadamente os resultados de seu trabalho. Se você trabalha com HTTP REST, existem dois formatos de anotação comuns para isso: RAML e Open API Specification (aka Swagger). Mas as questões que focamos hoje não estão vinculadas a nenhum protocolo específico. Portanto, o seguinte será relevante mesmo para o gRPC.

Antecedentes


Acronis existe há mais de 15 anos. Durante esse período, os produtos e a base de códigos evoluíram significativamente. A partir de um aplicativo de desktop complexo, chegamos a um modelo corporativo com consoles de gerenciamento centralizados, delimitação de direitos e logs de auditoria. O próximo passo foi a transformação do aplicativo corporativo em uma plataforma aberta, onde a experiência acumulada foi usada para integrar-se a serviços externos.

Se antes a API era importante, agora ela se tornou um componente crítico do produto. E os processos que essa API fornece amadureceram.

Questões principais


Os problemas em torno da criação da API parecem familiares a todos. Vamos descrevê-los de uma forma hipertrofiada: como dores que atormentam um programador hipotético Vasya e seu gerente não menos hipotético Kolya. Todos os nomes são fictícios e as correspondências não são acidentais :)

1. Descrição desatualizada

Deixe o programador Vasya desenvolver o componente A, que usa a API do componente B. O último possui uma anotação, mas é inválido. Vasya precisa se arrastar para o código de outra pessoa, procurar pessoas, fazer perguntas. Os prazos estão mudando, e seu gerente Kolya deve lidar com as transferências de prazos.

2. API não é consistente

O programador Vasya concluiu a tarefa e passou para a seguinte, relacionada à operação do componente B. Mas os desenvolvedores B e C têm um sentimento de beleza diferente, portanto as mesmas coisas são feitas de maneira diferente na API. Vasya novamente passou a lidar com o código, e Kolya novamente sofre de uma falha no cumprimento dos prazos.

3. API não documentada

O gerente de Kolya decide publicar a API do componente A para que os integradores possam fazer integrações maravilhosas. Os integradores enfrentam problemas, o serviço de suporte está sobrecarregado, tudo está pegando fogo com o gerente Kolya, e Vasya sente que sua vez chegará em breve.

4. A API não é compatível com a versão antiga.

As integrações são implementadas, todos os incêndios são extintos. Mas Vasya decide subitamente que a API de seu componente está longe de ser perfeita e está imersa em revisão. Obviamente, nesse caso, a compatibilidade com versões anteriores é violada e todas as integrações desmoronam. Essa situação leva a custos por parte dos integradores e a perda de dinheiro pela empresa de desenvolvimento.

Métodos de tratamento


Todos esses problemas surgem quando os programadores não têm idéia de uma boa API REST ou essa exibição é fragmentada. Na realidade, nem todos os desenvolvedores têm experiência com o REST. E, portanto, os principais métodos de "tratamento" são direcionados à educação. Quando a visão da API certa, coordenada com a visão de outros desenvolvedores, arquitetos e documentários, começa a ficar mais sensível na cabeça de cada desenvolvedor, a API se torna ideal. O processo de formação dessa visão exige esforços e ferramentas especializadas, sobre as quais falaremos agora.

Dor 1. A anotação não corresponde à implementação


A anotação pode diferir do estado atual do serviço, não apenas porque é uma API do "passado sombrio", que não pode ser alcançada. Também pode ser uma API brilhante do futuro que ainda não chegou.

A razão para essas condições é a falta de entendimento do motivo pelo qual as anotações são necessárias. Sem terror por parte dos arquitetos, os desenvolvedores tendem a considerar a anotação uma ferramenta auxiliar interna e não implica que ela seja usada por alguém de fora.

Você pode curar essa dor:

  • Revisão arquitetônica. Uma coisa muito útil para empresas de todos os tamanhos, nas quais há pelo menos um programador que "sabe fazer direito". Ao alterar o serviço, o arquiteto ou o responsável deve monitorar o status das anotações e lembrar aos programadores que é necessário atualizar não apenas o serviço, mas também sua descrição. Efeitos colaterais - um gargalo na cara do arquiteto
  • Geração de código a partir de anotações. Essa é a chamada abordagem API-first. Isso implica que você faça inicialmente uma anotação, gere o código principal (existem ferramentas suficientes para isso, por exemplo [go-swagger] (https://github.com/go-swagger/go-swagger)) e preencha o serviço comercial lógica. Esse arranjo evita inconsistências. Funciona bem quando a área de tarefas resolvidas pelo serviço é claramente delineada.
  • Testando anotações versus implementação . Para isso, geramos a partir da anotação (RAML / swagger) o cliente que bombardeia o serviço com solicitações. Se as respostas corresponderem à anotação e o serviço em si não cair, tudo estará bem.

Testando anotação x implementação
Vamos nos concentrar nos testes. Essa geração de consulta totalmente automática é uma tarefa complexa. Tendo dados de anotações da API, você pode criar solicitações separadas. No entanto, qualquer API implica dependências, por exemplo, antes de chamar GET / clients / {cliend_id}, você precisa primeiro criar este objeto e depois obter seu ID. Às vezes, as dependências são menos explícitas - criar um objeto X requer passar o identificador do objeto associado Y, e isso não é uma sub-coleção. Nem RAML nem Swagger permitem que dependências explícitas sejam descritas. Portanto, várias abordagens são possíveis aqui:

  1. Espere comentários formais dos desenvolvedores em anotações indicando dependências.
  2. Solicite uma descrição da sequência esperada aos desenvolvedores (existem várias maneiras de descrever solicitações usando YAML , DSL especializado ou por meio de uma bela GUI, como o apigee agora abandonado.
  3. Pegue dados reais (por exemplo, usando o OpenResty para registrar todas as solicitações e respostas do servidor)
  4. Extrair dependências da anotação usando (quase) inteligência artificial (por exemplo, RESTler )

De qualquer forma, a tarefa de teste consome muito tempo.

imagem

Pessoalmente, chegamos ao ponto de preparar as sequências de teste manualmente. De qualquer forma, os desenvolvedores precisam escrever testes, para que possamos fornecer a eles uma ferramenta conveniente que pode encontrar alguns bugs adicionais.

Nosso utilitário usa o seguinte yaml para descrever a sequência de solicitações:

imagem

Parênteses curvos declaram variáveis ​​que são substituídas durante o teste. A variável de endereço é passada como um parâmetro da CLI e gera aleatoriamente uma sequência arbitrária. De maior interesse aqui é o campo response-to-var: ele contém uma variável na qual json será gravado com a resposta do servidor. Portanto, na última linha, você pode obter o ID do objeto criado usando task.id.

Dor 2. A API não é consistente


O que é consistência? Não introduziremos nenhuma definição formal, mas, simplificando, isso é consistência interna. Por exemplo, no projeto inicial, Vasya precisava agregar dados em relatórios no HighLoad, e a API fornece filtragem de dados por ano. Depois que o projeto estava quase completo, o gerente Kolya foi até Vasya com um pedido para adicionar estatísticas sobre os alto-falantes à análise e criar um novo método "GET speakers" também com filtragem por ano. Como resultado, Vasya completa o código em algumas horas, mas durante o processo de teste, o método não funciona. A razão é que, em um caso, "ano" é um número, em outro, uma sequência. Mas isso, obviamente, não é óbvio à primeira vista e requer cuidados constantes ao trabalhar com a API. A persistência da API ocorre quando esse cuidado excessivo não é necessário.

Existem muitos exemplos de inconsistência:

  1. uso de diferentes formatos dos mesmos dados. Por exemplo, formato da hora, tipo de identificador (número ou string UUID),
  2. aplicar sintaxe diferente para filtragem ou paginação,
  3. esquemas de autorização diferentes para serviços. As diferenças não apenas pulverizam o cérebro dos programadores, mas também refletem nos testes que precisarão suportar diferentes esquemas.


Tratamento :

  • Revisão arquitetônica. Se houver um arquiteto tirano, ele (na ausência de esquizofrenia) garantirá consistência. Efeitos colaterais: fator de ônibus e tirania :)
  • Criando a API de diretrizes. Esse é um padrão único que precisa ser desenvolvido (ou preparado), mas o mais importante é implementá-lo. Isso requer propaganda, palito e cenoura.
  • Implementação de verificações estáticas para conformidade com as Diretrizes da API da anotação (leia sobre isso aqui ).

imagem
Exemplo - Itens de Teste Estático

Cada empresa faz sua própria escolha de qual Diretriz usar. E, provavelmente, não existe uma abordagem universal, o que deveria ser e o que não deveria. Afinal, quanto mais disposições no padrão, mais rigoroso você deve controlar e mais restringe a liberdade de criatividade. E o mais importante, poucas pessoas leem até o final um documento de "apenas 100 páginas".

Em nossa empresa, incluímos os seguintes pontos na diretriz:

imagem
Outros bons exemplos de diretrizes podem ser encontrados no Microsoft , PayPal , Google .

Dor 3. API não documentada


A existência de anotações é uma condição necessária, mas não suficiente, para a simplicidade do trabalho com a API. Você pode escrever uma anotação para que ela não realize todo o seu potencial. Isso acontece quando:

  1. descrições insuficientes (para parâmetros, cabeçalhos, erros etc.);
  2. não há exemplos suficientes de uso, porque o exemplo pode ser usado não apenas para melhorar a documentação (mais contexto para o desenvolvedor e a capacidade de jogar diretamente com a API diretamente do portal), mas também para testar (como ponto de partida para a difusão);
  3. existem recursos não documentados.

Normalmente, isso acontece quando os desenvolvedores não entendem claramente por que as anotações necessárias são necessárias, quando não há comunicação entre escritores técnicos e programadores e também se ninguém descobriu quanto custa a empresa com documentação insuficiente. E se eles chegassem ao programador e puxassem após cada solicitação de suporte, todas as anotações seriam preenchidas muito rapidamente.

Tratamento:

  • Disponibilidade de ferramentas de geração de referência da API para o programador. Se o desenvolvedor vir a aparência da descrição de sua API para colegas e usuários, ele tentará melhorar a anotação. Efeitos colaterais: A configuração dessas ferramentas exigirá trabalho manual adicional.
  • Estabelecendo interação entre todos os envolvidos : programadores, evangelistas, equipe de apoio. Efeitos colaterais: conhecer todos com todos, complicar processos.
  • Usando testes com base na anotação da API . Implementação das verificações estáticas acima nos repositórios de IC com anotações.

No Acronis, uma API de anotação é gerada com base em anotações com clientes SDK e seções Try-It. Juntamente com exemplos de código e descrições de casos de uso, eles formam uma gama completa de complementos necessários e convenientes para os programadores. Veja nosso portal em developer.acronis.com

imagem

Devo dizer que existe toda uma classe de ferramentas para gerar a referência da API. Algumas empresas desenvolvem essas ferramentas para suas próprias necessidades. Outros usam ferramentas bastante simples e gratuitas, como o Swagger Editor . Após uma longa pesquisa (muito longa), nós da Acronis decidimos pelo Apimatic.io, preferindo-o ao REST United, Mulesoft AnyPoint e outros.

Dor 4. Problemas de compatibilidade com versões anteriores


A compatibilidade com versões anteriores pode ser prejudicada devido a qualquer ninharia. Por exemplo, o programador Vasya escreve a palavra compatibilidade todas as vezes com um erro de digitação: compatibilidade. Esse erro de digitação é encontrado no código, nos comentários e em um parâmetro de consulta. Tendo notado um erro, Vasya substitui essa palavra ao longo do projeto e, sem olhar, envia as alterações para a produção. Obviamente, a compatibilidade com versões anteriores será comprometida e o serviço será interrompido por várias horas.

Por que esses eventos podem ocorrer? O principal motivo é um mal-entendido do ciclo de vida da API, que pode se manifestar na quebra de integrações, em políticas imprevisíveis de EOL (Fim da vida) e em versões obscuras da API.

Tratamento:

  • Revisão arquitetônica. Como sempre, a mão firme de um arquiteto pode impedir a compatibilidade com versões anteriores. No entanto, sua principal tarefa é explicar o custo do suporte a várias versões e procurar opções para fazer alterações sem interromper a API existente.
  • Verificação de compatibilidade com versões anteriores. Se a anotação da API contiver uma descrição atualizada, as violações de compatibilidade com versões anteriores poderão ser verificadas no estágio do IC;
  • Atualização oportuna da documentação. A referência e a descrição da API devem ser atualizadas ao mesmo tempo que o código de serviço é alterado. Para fazer isso, você pode pelo menos iniciar listas de verificação padronizadas, pelo menos configurar notificações de alterações, pelo menos treinar super-habilidades para gerar tudo de tudo ... Importante! O departamento de documentação deve estar ciente de todas as alterações planejadas para ter a oportunidade de planejar recursos para atualizar a documentação e escrever guias de atualização. O guia de atualização, testado e verificado, é um atributo triste de qualquer renomeação iniciada na API.

Gerenciamento de mudanças


As regras que descrevem as atividades associadas ao ciclo de vida da API são chamadas de políticas de gerenciamento de alterações.

imagem

Se você possui duas versões das anotações “atual” e “nova”, a verificação de compatibilidade com versões anteriores é tecnicamente simples: basta analisar as duas anotações e verificar se os campos necessários existem

imagem

Escrevemos uma ferramenta especial que permite comparar todos os parâmetros críticos para compatibilidade com versões anteriores no IC. Por exemplo, ao alterar o corpo da resposta na solicitação GET / healthcheck, uma mensagem semelhante à seguinte será exibida:

imagem

Conclusão


Todo arquiteto sonha em se livrar dos problemas da API. Todo gerente sonha em não conhecer os problemas da API. :). Existem muitos medicamentos, mas cada um tem seu próprio preço e seus efeitos colaterais. Compartilhamos nossas opções de tratamento para as doenças infantis mais simples com a API e, em seguida, surgem problemas mais sérios. Conclusões do nosso artigo "capitães": os problemas de API começam com o chefe e ensinar as boas práticas às pessoas é a principal garantia de sucesso. Tudo o resto é apenas uma questão de tecnologia. E que problemas você enfrentou e que meio de solução você escolheu em sua organização?

imagem
Medicamentos para a API ruim.

Teremos o maior prazer em quaisquer pensamentos, classificações, comentários, opiniões e perguntas!

Source: https://habr.com/ru/post/pt483380/


All Articles