Experiência no desenvolvimento do serviço Refund Tool com API assíncrona em Kafka

O que poderia forçar uma empresa tão grande como a Lamoda, com um processo simplificado e dezenas de serviços interligados, a mudar significativamente a sua abordagem? A motivação pode ser completamente diferente: desde legislativa até o desejo de experimentar inerente a todos os programadores.

Mas isso não significa que você não possa contar com benefícios adicionais. Sergey Zaika lhe dirá exatamente o que você pode ganhar se implementar a API orientada a eventos no Kafka (poucos). Definitivamente, também se falará sobre figurões e descobertas interessantes - o experimento não pode prescindir deles.

Experiência no desenvolvimento do serviço Refund Tool com API assíncrona em Kafka

Isenção de responsabilidade: este artigo é baseado em materiais de um encontro que Sergey realizou em novembro de 2018 no HighLoad++. A experiência ao vivo de Lamoda trabalhando com Kafka atraiu ouvintes não menos do que outras reportagens da programação. Achamos que este é um excelente exemplo do fato de que você pode e deve sempre encontrar pessoas com ideias semelhantes, e os organizadores do HighLoad++ continuarão a tentar criar uma atmosfera propícia para isso.

Sobre o processo

Lamoda é uma grande plataforma de e-commerce que possui contact center próprio, serviço de entrega (e muitos afiliados), um estúdio fotográfico, um enorme armazém, e tudo isso roda em software próprio. Existem dezenas de métodos de pagamento, parceiros b2b que podem utilizar alguns ou todos esses serviços e desejam saber informações atualizadas sobre seus produtos. Além disso, a Lamoda opera em três países além da Federação Russa e lá tudo é um pouco diferente. No total, existem provavelmente mais de cem maneiras de configurar um novo pedido, que deve ser processado à sua maneira. Tudo isso funciona com a ajuda de dezenas de serviços que às vezes se comunicam de maneiras não óbvias. Existe também um sistema central cuja principal responsabilidade é o status dos pedidos. Nós a chamamos de BOB, eu trabalho com ela.

Ferramenta de reembolso com API orientada a eventos

A palavra orientada por eventos é bastante banal; um pouco mais adiante definiremos com mais detalhes o que isso significa. Começarei com o contexto em que decidimos experimentar a abordagem de API orientada a eventos no Kafka.

Experiência no desenvolvimento do serviço Refund Tool com API assíncrona em Kafka

Em qualquer loja, além dos pedidos pelos quais os clientes pagam, há momentos em que a loja é obrigada a devolver o dinheiro porque o produto não agradou ao cliente. Este é um processo relativamente curto: esclarecemos as informações, se necessário, e transferimos o dinheiro.

Mas o retorno ficou mais complicado devido a mudanças na legislação, e tivemos que implementar um microsserviço separado para isso.

Experiência no desenvolvimento do serviço Refund Tool com API assíncrona em Kafka

Nossa motivação:

  1. Lei FZ-54 - em suma, a lei exige a comunicação à repartição de finanças sobre cada transação monetária, seja uma declaração ou um recibo, num SLA bastante curto de alguns minutos. Nós, como empresa de comércio eletrônico, realizamos muitas operações. Tecnicamente, isto significa uma nova responsabilidade (e, portanto, um novo serviço) e melhorias em todos os sistemas envolvidos.
  2. Divisão BOB é um projeto interno da empresa para aliviar o BOB de um grande número de responsabilidades não essenciais e reduzir sua complexidade geral.

Experiência no desenvolvimento do serviço Refund Tool com API assíncrona em Kafka

Este diagrama mostra os principais sistemas Lamoda. Agora a maioria deles é mais uma constelação de 5 a 10 microsserviços em torno de um monólito cada vez menor. Eles estão crescendo lentamente, mas estamos tentando diminuí-los, porque implantar o fragmento selecionado no meio é assustador - não podemos permitir que ele caia. Somos obrigados a reservar todas as trocas (setas) e ter em conta que alguma delas pode ficar indisponível.

O BOB também possui muitas trocas: sistemas de pagamento, sistemas de entrega, sistemas de notificação, etc.

Tecnicamente BOB é:

  • ~ 150 mil linhas de código + ~ 100 mil linhas de testes;
  • php7.2 + Zend 1 e componentes Symfony 3;
  • >100 APIs e aproximadamente 50 integrações externas;
  • 4 países com lógica de negócio própria.

Implantar o BOB é caro e doloroso, a quantidade de código e problemas que ele resolve é tal que ninguém consegue colocar tudo na cabeça. Em geral, existem muitas razões para simplificá-lo.

Processo de devolução

Inicialmente, dois sistemas estão envolvidos no processo: BOB e Pagamento. Agora aparecem mais dois:

  • Serviço de Fiscalização, que cuidará de problemas de fiscalização e comunicação com serviços externos.
  • Ferramenta de Reembolso, que simplesmente contém novas exchanges para não inflacionar o BOB.

Agora o processo fica assim:

Experiência no desenvolvimento do serviço Refund Tool com API assíncrona em Kafka

  1. BOB recebe um pedido de reembolso.
  2. BOB fala sobre esta ferramenta de reembolso.
  3. A ferramenta de reembolso informa ao pagamento: “Devolva o dinheiro”.
  4. O pagamento devolve o dinheiro.
  5. A Refund Tool e o BOB sincronizam os status entre si, porque por enquanto ambos precisam disso. Ainda não estamos prontos para mudar completamente para a Ferramenta de Reembolso, pois o BOB possui UI, relatórios para contabilidade e, em geral, muitos dados que não podem ser transferidos tão facilmente. Você tem que sentar em duas cadeiras.
  6. O pedido de fiscalização desaparece.

Como resultado, fizemos uma espécie de ônibus de eventos em Kafka - ônibus de eventos, onde tudo começou. Viva, agora temos um único ponto de falha (sarcasmo).

Experiência no desenvolvimento do serviço Refund Tool com API assíncrona em Kafka

Os prós e contras são bastante óbvios. Fizemos um ônibus, o que significa que agora todos os serviços dependem dele. Isso simplifica o projeto, mas introduz um único ponto de falha no sistema. Kafka irá travar, o processo irá parar.

O que é uma API orientada a eventos

Uma boa resposta a esta pergunta está no relatório de Martin Fowler (GOTO 2017) "Os muitos significados da arquitetura orientada a eventos".

Resumidamente o que fizemos:

  1. Conclua todas as trocas assíncronas via armazenamento de eventos. Em vez de informar todos os consumidores interessados ​​sobre uma mudança de status na rede, escrevemos um evento sobre uma mudança de status para um armazenamento centralizado, e os consumidores interessados ​​no tópico leem tudo o que aparece a partir daí.
  2. O evento neste caso é uma notificação (notificações) que algo mudou em algum lugar. Por exemplo, o status do pedido mudou. O consumidor que estiver interessado em alguns dados que acompanham a mudança de status e não incluídos na notificação pode descobrir ele mesmo o seu status.
  3. A opção máxima é o fornecimento completo de eventos, transferência de estado, evento em que contém todas as informações necessárias para o processamento: de onde veio e para que status foi, como exatamente os dados foram alterados, etc. A única questão é a viabilidade e a quantidade de informações que você pode armazenar.

Como parte do lançamento da Ferramenta de Reembolso, utilizamos a terceira opção. Isso simplificou o processamento de eventos, pois não havia necessidade de extrair informações detalhadas, além de eliminar o cenário em que cada novo evento gerava uma explosão de solicitações de esclarecimento dos consumidores.

Serviço de ferramenta de reembolso não carregado, então Kafka é mais um gosto de caneta do que uma necessidade. Não creio que se o serviço de reembolso se tornasse um projeto de alta carga, as empresas ficariam satisfeitas.

Troca assíncrona COMO ESTÁ

Para trocas assíncronas, o departamento de PHP geralmente usa RabbitMQ. Coletamos os dados da solicitação, colocamos em uma fila e o consumidor do mesmo serviço leu e enviou (ou não enviou). Para a API em si, a Lamoda usa ativamente o Swagger. Projetamos uma API, descrevemos-na em Swagger e geramos código de cliente e servidor. Também usamos um JSON RPC 2.0 ligeiramente aprimorado.

Em alguns lugares são usados ​​barramentos ESB, alguns vivem no activeMQ, mas, em geral, RabbitMQ - padrão.

Troca assíncrona TO BE

Ao projetar a troca via barramento de eventos, uma analogia pode ser traçada. Da mesma forma, descrevemos a troca futura de dados por meio de descrições de estruturas de eventos. No formato yaml, tivemos que fazer nós mesmos a geração do código, o gerador cria DTOs de acordo com a especificação e ensina clientes e servidores a trabalhar com eles. A geração entra em dois idiomas - golang e php. Isso ajuda a manter as bibliotecas consistentes. O gerador é escrito em golang, por isso recebeu o nome de gogi.

O fornecimento de eventos no Kafka é algo típico. Existe uma solução da versão empresarial principal do Kafka Confluent, existe nakadi, uma solução dos nossos irmãos de domínio Zalando. Nosso motivação para começar com vanilla Kafka - isto significa deixar a solução livre até decidirmos finalmente se a iremos utilizar em todo o lado, e também deixar-nos margem de manobra e melhorias: queremos apoio para os nossos JSON RPC 2.0, geradores para duas linguagens e vamos ver o que mais.

É irónico que mesmo num caso tão feliz, quando existe uma empresa aproximadamente semelhante, a Zalando, que criou uma solução aproximadamente semelhante, não possamos utilizá-la de forma eficaz.

O padrão de arquitetura no lançamento é o seguinte: lemos diretamente do Kafka, mas escrevemos apenas através do barramento de eventos. Tem muita coisa pronta para leitura no Kafka: corretoras, balanceadores, e está mais ou menos pronto para escalonamento horizontal, queria manter isso. Queríamos completar a gravação através de um Gateway, também conhecido como barramento de eventos, e aqui está o porquê.

Ônibus de eventos

Ou um ônibus de eventos. Este é simplesmente um gateway http sem estado, que desempenha várias funções importantes:

  • Produzindo Validação — verificamos se os eventos atendem às nossas especificações.
  • Sistema mestre de eventos, ou seja, este é o principal e único sistema da empresa que responde à questão de quais eventos com quais estruturas são considerados válidos. A validação envolve simplesmente tipos de dados e enumerações para especificar estritamente o conteúdo.
  • Função hash para fragmentação - a estrutura da mensagem Kafka é valor-chave e usando o hash da chave é calculado onde colocá-la.

Sua marca

Trabalhamos em uma grande empresa com um processo simplificado. Por que mudar alguma coisa? Este é um experimentoe esperamos colher vários benefícios.

Trocas 1:n+1 (uma para muitas)

Kafka torna muito fácil conectar novos consumidores à API.

Digamos que você tenha um diretório que precisa manter atualizado em vários sistemas ao mesmo tempo (e em alguns novos). Anteriormente, inventamos um pacote que implementava set-API, e o sistema mestre era informado dos endereços dos consumidores. Agora o sistema mestre envia atualizações sobre o tema e todos os interessados ​​leem. Um novo sistema apareceu - nós o inscrevemos no tópico. Sim, também pacote, mas mais simples.

No caso da ferramenta de reembolso, que é um pedaço do BOB, é conveniente mantê-los sincronizados através do Kafka. O pagamento diz que o dinheiro foi devolvido: BOB, RT souberam disso, mudaram de status, o Serviço de Fiscalização descobriu e emitiu um cheque.

Experiência no desenvolvimento do serviço Refund Tool com API assíncrona em Kafka

Temos planos de criar um Serviço de Notificações unificado que notificaria o cliente sobre novidades relacionadas ao seu pedido/devoluções. Agora esta responsabilidade está espalhada entre os sistemas. Será suficiente ensinarmos o Serviço de Notificações a capturar informações relevantes do Kafka e respondê-las (e desabilitar essas notificações em outros sistemas). Não serão necessárias novas trocas diretas.

Baseado em dados

As informações entre sistemas tornam-se transparentes - não importa que “empresa sangrenta” você tenha e não importa quão grande seja sua lista de pendências. A Lamoda possui um departamento de Data Analytics que coleta dados de sistemas e os coloca de forma reutilizável, tanto para negócios quanto para sistemas inteligentes. Kafka permite que você forneça muitos dados rapidamente e mantenha o fluxo de informações atualizado.

Log de replicação

As mensagens não desaparecem após serem lidas, como no RabbitMQ. Quando um evento contém informações suficientes para processamento, temos um histórico das alterações recentes no objeto e, se desejar, a capacidade de aplicar essas alterações.

O período de armazenamento do log de replicação depende da intensidade da gravação neste tópico; o Kafka permite definir limites de maneira flexível para o tempo de armazenamento e o volume de dados. Para temas intensivos, é importante que todos os consumidores tenham tempo para ler a informação antes que ela desapareça, mesmo no caso de inoperabilidade de curto prazo. Geralmente é possível armazenar dados para unidades de dias, o que é suficiente para suporte.

Experiência no desenvolvimento do serviço Refund Tool com API assíncrona em Kafka

A seguir, uma pequena releitura da documentação, para quem não conhece o Kafka (a foto também é da documentação)

AMQP possui filas: escrevemos mensagens em uma fila para o consumidor. Normalmente, uma fila é processada por um sistema com a mesma lógica de negócios. Se precisar notificar vários sistemas, você pode ensinar o aplicativo a gravar em várias filas ou configurar o exchange com o mecanismo fanout, que os clona sozinho.

Kafka tem uma abstração semelhante tópico, em que você escreve mensagens, mas elas não desaparecem após a leitura. Por padrão, ao se conectar ao Kafka, você recebe todas as mensagens e tem a opção de salvar de onde parou. Ou seja, você lê sequencialmente, não pode marcar a mensagem como lida, mas sim salvar o id a partir do qual poderá continuar lendo. O ID que você definiu é chamado de offset e o mecanismo é commit offset.

Conseqüentemente, lógicas diferentes podem ser implementadas. Por exemplo, temos BOB em 4 instâncias para países diferentes - Lamoda está na Rússia, Cazaquistão, Ucrânia, Bielorrússia. Como são implantados separadamente, eles têm configurações ligeiramente diferentes e sua própria lógica de negócios. Indicamos na mensagem a que país se refere. Cada consumidor BOB em cada país lê com um groupId diferente e, se a mensagem não se aplica a eles, eles a ignoram, ou seja, confirma imediatamente o deslocamento +1. Se o mesmo tópico for lido pelo nosso Serviço de Pagamento, então o faz com um grupo separado e, portanto, as compensações não se cruzam.

Requisitos do evento:

  • Completude dos dados. Gostaria que o evento tivesse dados suficientes para que pudesse ser processado.

  • Integridade Delegamos ao Events-bus a verificação de que o evento é consistente e pode processá-lo.
  • A ordem é importante. No caso de um regresso, somos obrigados a trabalhar com a história. Nas notificações o pedido não tem importância, se forem notificações homogêneas o email será o mesmo independente de qual pedido chegou primeiro. No caso de um reembolso, existe um processo claro; se alterarmos a encomenda, surgirão exceções, o reembolso não será criado ou processado - terminaremos num estado diferente.
  • Consistência. Temos uma loja e agora criamos eventos em vez de uma API. Precisamos de uma forma de transmitir aos nossos serviços informações de forma rápida e barata sobre novos eventos e alterações nos já existentes. Isso é conseguido por meio de uma especificação comum em um repositório git separado e geradores de código. Portanto, clientes e servidores em diferentes serviços são coordenados.

Kafka em Lamoda

Temos três instalações Kafka:

  1. Histórico;
  2. P&D;
  3. Ônibus de eventos.

Hoje estamos falando apenas do último ponto. No barramento de eventos não temos instalações muito grandes - 3 corretores (servidores) e apenas 27 tópicos. Via de regra, um tópico é um processo. Mas este é um ponto sutil e iremos abordá-lo agora.

Experiência no desenvolvimento do serviço Refund Tool com API assíncrona em Kafka

Acima está o gráfico rps. O processo de reembolso é marcado com uma linha turquesa (sim, aquela no eixo X), e a linha rosa é o processo de atualização de conteúdo.

O catálogo Lamoda contém milhões de produtos e os dados são atualizados o tempo todo. Algumas coleções saem de moda, novas são lançadas para substituí-las e novos modelos aparecem constantemente no catálogo. Tentamos prever o que será interessante para nossos clientes amanhã, por isso compramos constantemente coisas novas, fotografamos e atualizamos a vitrine.

Os picos rosa são atualizações de produtos, ou seja, mudanças nos produtos. Vê-se que os caras tiraram fotos, tiraram fotos e depois de novo! – carregou um pacote de eventos.

Casos de uso de eventos Lamoda

Usamos a arquitetura construída para as seguintes operações:

  • Rastreamento de status de retorno: call to action e rastreamento de status de todos os sistemas envolvidos. Pagamento, status, fiscalização, notificações. Aqui testamos a abordagem, criamos ferramentas, coletamos todos os bugs, escrevemos documentação e explicamos aos nossos colegas como usá-la.
  • Atualizando cartões de produto: configuração, metadados, características. Um sistema lê (que exibe) e vários escrevem.
  • E-mail, push e sms: a encomenda foi recolhida, a encomenda chegou, a devolução foi aceite, etc., são muitos.
  • Estoque, renovação de armazém — atualização quantitativa de itens, apenas números: chegada ao armazém, devolução. É necessário que todos os sistemas associados à reserva de mercadorias operem com os dados mais atualizados. Atualmente, o sistema de atualização de estoque é bastante complexo; Kafka irá simplificá-lo.
  • Análise de Dados (departamento de P&D), ferramentas de ML, análises, estatísticas. Queremos que as informações sejam transparentes - Kafka é adequado para isso.

Agora, a parte mais interessante sobre os grandes solavancos e descobertas interessantes que ocorreram nos últimos seis meses.

Problemas de projeto

Digamos que queremos fazer algo novo - por exemplo, transferir todo o processo de entrega para Kafka. Agora parte do processo é implementado no Processamento de Pedidos no BOB. Existe um modelo de status por trás da transferência de um pedido para o serviço de entrega, movimentação para um armazém intermediário e assim por diante. Existe um monólito inteiro, até dois, além de um monte de APIs dedicadas à entrega. Eles sabem muito mais sobre entrega.

Estas parecem ser áreas semelhantes, mas o Processamento de Pedidos no BOB e o Sistema de Remessa têm status diferentes. Por exemplo, alguns serviços de correio não enviam status intermediários, mas apenas os finais: “entregue” ou “perdido”. Outros, pelo contrário, relatam detalhadamente a movimentação de mercadorias. Cada um tem suas próprias regras de validação: para alguns, o email é válido, o que significa que será processado; para outros não é válido, mas o pedido ainda será processado porque existe um número de telefone para contato, e alguém dirá que tal pedido não será processado de forma alguma.

Fluxo de dados

No caso de Kafka, surge a questão da organização do fluxo de dados. Esta tarefa envolve escolher uma estratégia baseada em vários pontos; vamos passar por todos eles.

Em um tópico ou em diferentes?

Temos uma especificação de evento. No BOB escrevemos que tal e tal pedido precisa ser entregue, e indicamos: o número do pedido, sua composição, alguns SKUs e códigos de barras, etc. Quando a mercadoria chegar ao armazém, a entrega poderá receber status, carimbos de data e hora e tudo o que for necessário. Mas então queremos receber atualizações sobre esses dados no BOB. Temos um processo inverso de recebimento de dados desde a entrega. Este é o mesmo evento? Ou esta é uma troca separada que merece um tópico próprio?

Muito provavelmente, eles serão muito semelhantes, e a tentação de criar um tópico não é infundada, porque um tópico separado significa consumidores separados, configurações separadas, uma geração separada de tudo isso. Mas não é um fato.

Novo campo ou novo evento?

Mas se você usar os mesmos eventos, surge outro problema. Por exemplo, nem todos os sistemas de entrega podem gerar o tipo de DTO que o BOB pode gerar. Enviamos o id para eles, mas eles não o salvam porque não precisam e, do ponto de vista de iniciar o processo do barramento de eventos, este campo é obrigatório.

Se introduzirmos uma regra para o barramento de eventos de que este campo é obrigatório, seremos forçados a definir regras de validação adicionais no BOB ou no manipulador de eventos de início. A validação começa a se espalhar por todo o serviço - isso não é muito conveniente.

Outro problema é a tentação do desenvolvimento incremental. Dizem-nos que algo precisa ser acrescentado ao evento e talvez, se pensarmos bem, deveria ter sido um evento separado. Mas em nosso esquema, um evento separado é um tópico separado. Um tópico separado é todo o processo que descrevi acima. O desenvolvedor fica tentado a simplesmente adicionar outro campo ao esquema JSON e regenerá-lo.

No caso de reembolsos, chegamos ao evento em meio ano. Tivemos um metaevento chamado atualização de reembolso, que tinha um campo de tipo descrevendo o que realmente era essa atualização. Por conta disso, tivemos switches “maravilhosos” com validadores que nos disseram como validar esse evento com esse tipo.

Versionamento de eventos

Para validar mensagens no Kafka você pode usar Avro, mas foi necessário colocá-lo imediatamente e usar o Confluent. No nosso caso, temos que ter cuidado com o versionamento. Nem sempre será possível reler as mensagens do log de replicação porque o modelo “saiu”. Basicamente, trata-se de construir versões para que o modelo seja compatível com versões anteriores: por exemplo, tornar um campo temporariamente opcional. Se as diferenças forem muito fortes, começamos a escrever um novo tópico e transferimos os clientes quando terminam de ler o antigo.

Ordem de leitura garantida das partições

Os tópicos dentro do Kafka são divididos em partições. Isto não é muito importante enquanto estamos projetando entidades e bolsas, mas é importante ao decidir como consumi-las e escaloná-las.

Normalmente, você escreve um tópico no Kafka. Por padrão, uma partição é usada e todas as mensagens neste tópico vão para ela. E o consumidor consequentemente lê essas mensagens sequencialmente. Digamos que agora precisamos expandir o sistema para que as mensagens sejam lidas por dois consumidores diferentes. Se, por exemplo, você estiver enviando SMS, poderá dizer ao Kafka para criar uma partição adicional, e o Kafka começará a dividir as mensagens em duas partes - metade aqui, metade aqui.

Como Kafka os divide? Cada mensagem possui um corpo (no qual armazenamos JSON) e uma chave. Você pode anexar uma função hash a esta chave, que determinará em qual partição a mensagem irá.

No nosso caso de reembolso isso é importante, se pegarmos duas partições, então existe a chance de um consumidor paralelo processar o segundo evento antes do primeiro e haverá problemas. A função hash garante que mensagens com a mesma chave acabem na mesma partição.

Eventos vs comandos

Este é outro problema que encontramos. Evento é um determinado evento: dizemos que algo aconteceu em algum lugar (something_happened), por exemplo, um item foi cancelado ou ocorreu um reembolso. Se alguém ouvir esses eventos, então de acordo com “item cancelado”, a entidade de reembolso será criada e “reembolso ocorrido” será escrito em algum lugar nas configurações.

Mas normalmente, quando você projeta eventos, você não quer escrevê-los em vão - você confia no fato de que alguém os lerá. Há uma grande tentação de escrever não algo_aconteceu (item_cancelado, reembolso_reembolsado), mas algo_deveria_ser_feito. Por exemplo, o item está pronto para ser devolvido.

Por um lado, sugere como o evento será utilizado. Por outro lado, parece muito menos com um nome de evento normal. Além disso, não está longe daqui o comando do_something. Mas você não tem garantia de que alguém leu este evento; e se você leu, então você leu com sucesso; e se você leu com sucesso, então você fez algo, e esse algo foi bem sucedido. No momento em que um evento se torna algo, o feedback se torna necessário, e isso é um problema.

Experiência no desenvolvimento do serviço Refund Tool com API assíncrona em Kafka

Na troca assíncrona no RabbitMQ, ao ler a mensagem, vá em http, você tem uma resposta - pelo menos que a mensagem foi recebida. Quando você escreve para Kafka, há uma mensagem que você escreveu para Kafka, mas você não sabe nada sobre como ela foi processada.

Portanto, no nosso caso, tivemos que introduzir um evento de resposta e configurar o monitoramento para que se tantos eventos fossem enviados, depois de tal e tal horário chegasse o mesmo número de eventos de resposta. Se isso não acontecer, então algo parece ter dado errado. Por exemplo, se enviamos o evento “item_ready_to_refund”, esperamos que um reembolso seja criado, o dinheiro seja devolvido ao cliente e o evento “money_refunded” nos seja enviado. Mas isto não é certo, pelo que é necessária monitorização.

Nuances

Há um problema bastante óbvio: se você ler um tópico sequencialmente e tiver alguma mensagem ruim, o consumidor cairá e você não irá mais longe. Você precisa parar todos os consumidores, confirme o deslocamento ainda mais para continuar lendo.

Sabíamos disso, contávamos com isso e, mesmo assim, aconteceu. E isso aconteceu porque o evento era válido do ponto de vista do barramento de eventos, o evento era válido do ponto de vista do validador da aplicação, mas não era válido do ponto de vista do PostgreSQL, porque no nosso sistema MySQL com UNSIGNED INT, e no recém-escrito o sistema tinha PostgreSQL apenas com INT. O tamanho dele é um pouco menor, e o Id não serviu. Symfony morreu com uma exceção. É claro que capturamos a exceção porque confiamos nela e íamos confirmar esse deslocamento, mas antes disso queríamos incrementar o contador de problemas, pois a mensagem foi processada sem sucesso. Os contadores neste projeto também estão no banco de dados, e o Symfony já fechou a comunicação com o banco de dados, e a segunda exceção encerrou todo o processo sem chance de cometer offset.

O serviço ficou parado por algum tempo - felizmente, com Kafka isso não é tão ruim, porque as mensagens permanecem. Quando o trabalho for restaurado, você poderá terminar de lê-los. É confortável.

Kafka tem a capacidade de definir um deslocamento arbitrário por meio de ferramentas. Mas, para isso, é necessário parar todos os consumidores - no nosso caso, preparar um lançamento separado, no qual não haverá consumidores, reimplantações. Então, no Kafka, você pode mudar o deslocamento por meio de ferramentas e a mensagem será transmitida.

Outra nuance - log de replicação vs rdkafka.so - está relacionado às especificidades do nosso projeto. Usamos PHP, e no PHP, via de regra, todas as bibliotecas se comunicam com o Kafka por meio do repositório rdkafka.so, e então existe algum tipo de wrapper. Talvez sejam essas as nossas dificuldades pessoais, mas descobriu-se que simplesmente reler um trecho do que já lemos não é tão fácil. Em geral, houve problemas de software.

Voltando às especificidades do trabalho com partições, está escrito na documentação consumidores >= partições de tópico. Mas descobri isso muito mais tarde do que gostaria. Se quiser escalar e ter dois consumidores, você precisará de pelo menos duas partições. Ou seja, se você tinha uma partição na qual se acumularam 20 mil mensagens e fez uma nova, o número de mensagens não será equalizado tão cedo. Portanto, para ter dois consumidores paralelos, é necessário lidar com partições.

Monitoramento

Penso que a forma como monitorizamos ficará ainda mais clara sobre os problemas que existem na abordagem existente.

Por exemplo, calculamos quantos produtos no banco de dados mudaram recentemente de status e, consequentemente, os eventos deveriam ter ocorrido com base nessas alterações, e enviamos esse número para nosso sistema de monitoramento. Então, de Kafka obtemos o segundo número, quantos eventos foram realmente registrados. Obviamente, a diferença entre estes dois números deve ser sempre zero.

Experiência no desenvolvimento do serviço Refund Tool com API assíncrona em Kafka

Além disso, você precisa monitorar o desempenho do produtor, se o barramento de eventos recebeu mensagens e o desempenho do consumidor. Por exemplo, nos gráficos abaixo, a Refund Tool está indo bem, mas o BOB claramente tem alguns problemas (picos azuis).

Experiência no desenvolvimento do serviço Refund Tool com API assíncrona em Kafka

Já mencionei o atraso do grupo de consumidores. Grosso modo, este é o número de mensagens não lidas. Em geral, nossos consumidores trabalham rapidamente, então o atraso costuma ser 0, mas às vezes pode haver um pico de curto prazo. Kafka pode fazer isso imediatamente, mas você precisa definir um determinado intervalo.

Existe um projeto Tocaque lhe dará mais informações sobre Kafka. Ele simplesmente usa a API do grupo de consumidores para fornecer o status do desempenho desse grupo. Além de OK e Failed, há um aviso, e você pode descobrir que seus consumidores não conseguem acompanhar o ritmo de produção - eles não têm tempo para revisar o que está escrito. O sistema é bastante inteligente e fácil de usar.

Experiência no desenvolvimento do serviço Refund Tool com API assíncrona em Kafka

Esta é a aparência da resposta da API. Aqui está o grupo bob-live-fifa, partição refund.update.v1, status OK, lag 0 - o último deslocamento final tal e tal.

Experiência no desenvolvimento do serviço Refund Tool com API assíncrona em Kafka

Monitoramento atualizado_no SLA (travado) Eu já mencionei. Por exemplo, o produto mudou para o status pronto para devolução. Instalamos o Cron, que diz que se em 5 minutos esse objeto não for reembolsado (devolvemos o dinheiro através de sistemas de pagamento muito rapidamente), então algo definitivamente deu errado, e este é definitivamente um caso para suporte. Portanto, simplesmente pegamos o Cron, que lê essas coisas, e se forem maiores que 0, envia um alerta.

Para resumir, usar eventos é conveniente quando:

  • a informação é necessária para vários sistemas;
  • o resultado do processamento não é importante;
  • há poucos eventos ou pequenos eventos.

Parece que o artigo tem um tópico muito específico - API assíncrona no Kafka, mas em relação a ele gostaria de recomendar muitas coisas ao mesmo tempo.
Primeiro, próximo HighLoad ++ precisamos esperar até novembro, em abril haverá uma versão de São Petersburgo e em junho falaremos sobre cargas elevadas em Novosibirsk.
Em segundo lugar, o autor do relatório, Sergei Zaika, é membro do Comité do Programa da nossa nova conferência sobre gestão do conhecimento KnowledgeConf. A conferência tem duração de um dia, acontecerá no dia 26 de abril, mas sua programação é muito intensa.
E será em maio PHP Rússia и RIT++ (com DevOpsConf incluído) - você também pode sugerir seu tema lá, falar sobre sua experiência e reclamar dos seus cones recheados.

Fonte: habr.com

Adicionar um comentário