Experiencia no desenvolvemento do servizo Refund Tool cunha API asíncrona en Kafka

Que podería obrigar a unha empresa tan grande como Lamoda, cun proceso racionalizado e decenas de servizos interconectados, a cambiar significativamente o seu enfoque? A motivación pode ser completamente diferente: desde a lexislativa ata o desexo de experimentar inherente a todos os programadores.

Pero isto non significa que non poida contar con beneficios adicionais. Sergey Zaika diráche exactamente o que podes gañar se implementas a API dirixida por eventos en Kafka (poucos). Definitivamente tamén se falará de grandes tiros e descubrimentos interesantes: o experimento non pode prescindir deles.

Experiencia no desenvolvemento do servizo Refund Tool cunha API asíncrona en Kafka

Exención de responsabilidade: este artigo baséase en materiais dunha reunión que Sergey celebrou en novembro de 2018 en HighLoad++. A experiencia en directo de Lamoda de traballar con Kafka atraeu aos oíntes nada menos que outros informes sobre a programación. Cremos que este é un excelente exemplo do feito de que sempre podes e debes atopar persoas con ideas afines, e os organizadores de HighLoad++ seguirán tratando de crear un ambiente propicio para iso.

Sobre o proceso

Lamoda é unha gran plataforma de comercio electrónico que ten o seu propio centro de contacto, servizo de entrega (e moitos afiliados), un estudo fotográfico, un enorme almacén e todo isto funciona co seu propio software. Hai decenas de métodos de pago, socios b2b que poden utilizar algúns ou todos estes servizos e queren coñecer información actualizada sobre os seus produtos. Ademais, Lamoda opera en tres países ademais da Federación Rusa e alí todo é un pouco diferente. En total, probablemente haxa máis de cen formas de configurar un novo pedido, que debe ser procesado ao seu xeito. Todo isto funciona coa axuda de decenas de servizos que ás veces se comunican de forma non obvia. Tamén existe un sistema central que ten como principal responsabilidade os estados das ordes. Chamámoslle BOB, traballo con ela.

Ferramenta de reembolso con API baseada en eventos

A palabra impulsada por eventos é bastante manida; un pouco máis adiante definiremos con máis detalle o que se quere dicir con isto. Comezarei polo contexto no que decidimos probar o enfoque da API baseada en eventos en Kafka.

Experiencia no desenvolvemento do servizo Refund Tool cunha API asíncrona en Kafka

En calquera tenda, ademais dos pedidos polos que pagan os clientes, hai momentos nos que a tenda está obrigada a devolver diñeiro porque o produto non lle convén ao cliente. Este é un proceso relativamente curto: aclaramos a información, se é necesario, e transferimos o diñeiro.

Pero a devolución fíxose máis complicada debido aos cambios na lexislación, e tivemos que implementar un microservizo separado para iso.

Experiencia no desenvolvemento do servizo Refund Tool cunha API asíncrona en Kafka

A nosa motivación:

  1. Lei FZ-54 - En resumo, a lei obriga a informar á oficina de Facenda sobre cada transacción monetaria, xa sexa unha devolución ou un recibo, nun SLA bastante curto de poucos minutos. Nós, como empresa de comercio electrónico, realizamos bastantes operacións. Tecnicamente, isto supón unha nova responsabilidade (e polo tanto un novo servizo) e melloras en todos os sistemas implicados.
  2. BOB dividido é un proxecto interno da empresa para relevar a BOB dun gran número de responsabilidades non básicas e reducir a súa complexidade xeral.

Experiencia no desenvolvemento do servizo Refund Tool cunha API asíncrona en Kafka

Este diagrama mostra os principais sistemas Lamoda. Agora a maioría son máis unha constelación de 5-10 microservizos arredor dun monólito cada vez menor. Están crecendo lentamente, pero estamos tentando facelas máis pequenas, porque despregar o fragmento seleccionado no medio dá medo: non podemos permitir que caia. Estamos obrigados a reservar todos os intercambios (frechas) e ter en conta o feito de que algún deles pode resultar non dispoñible.

BOB tamén ten bastantes intercambios: sistemas de pago, sistemas de entrega, sistemas de notificación, etc.

Tecnicamente BOB é:

  • ~150k liñas de código + ~100k liñas de probas;
  • php7.2 + Zend 1 e compoñentes de Symfony 3;
  • >100 API e ~50 integracións de saída;
  • 4 países con lóxica empresarial propia.

Implantar BOB é caro e doloroso, a cantidade de código e problemas que resolve é tal que ninguén se lle pode poñer todo na cabeza. En xeral, hai moitas razóns para simplificalo.

Proceso de devolución

Inicialmente, no proceso interveñen dous sistemas: BOB e Payment. Agora aparecen dous máis:

  • Servizo de Fiscalización, que se ocupará dos problemas de fiscalización e comunicación con servizos externos.
  • Ferramenta de reembolso, que simplemente contén novos intercambios para non inflar o BOB.

Agora o proceso ten o seguinte aspecto:

Experiencia no desenvolvemento do servizo Refund Tool cunha API asíncrona en Kafka

  1. BOB recibe unha solicitude de reembolso.
  2. BOB fala sobre esta ferramenta de reembolso.
  3. A ferramenta de reembolso indica a Payment: "Devolve o diñeiro".
  4. O pago devolve o diñeiro.
  5. Refund Tool e BOB sincronizan os estados entre si, porque polo momento ambos o necesitan. Aínda non estamos preparados para cambiar completamente á ferramenta de reembolso, xa que BOB ten unha interface de usuario, informes para a contabilidade e, en xeral, moitos datos que non se poden transferir tan facilmente. Tes que sentarte en dúas cadeiras.
  6. A solicitude de fiscalización desaparece.

Como resultado, fixemos unha especie de autobús de eventos en Kafka - autobús de eventos, no que comezou todo. Vaia, agora temos un único punto de fracaso (sarcasmo).

Experiencia no desenvolvemento do servizo Refund Tool cunha API asíncrona en Kafka

Os pros e os contras son bastante obvios. Fixemos un autobús, o que significa que agora todos os servizos dependen del. Isto simplifica o deseño, pero introduce un único punto de fallo no sistema. Kafka fallará, o proceso pararase.

Que é unha API dirixida por eventos

Unha boa resposta a esta pregunta está no informe de Martin Fowler (GOTO 2017) "Os moitos significados da arquitectura impulsada por eventos".

Brevemente o que fixemos:

  1. Completa todos os intercambios asíncronos mediante almacenamento de eventos. En lugar de informar a todos os consumidores interesados ​​sobre un cambio de estado a través da rede, escribimos un evento sobre un cambio de estado nun almacenamento centralizado e os consumidores interesados ​​no tema len todo o que aparece a partir de aí.
  2. O evento neste caso é unha notificación (notificacións) que algo cambiou nalgún lugar. Por exemplo, o estado da orde cambiou. Un consumidor que estea interesado nalgúns datos que acompañen o cambio de estado que non estean incluídos na notificación pode coñecer o seu estado el mesmo.
  3. A opción máxima é a fonte de eventos de pleno dereito, transferencia estatal, en cuxo caso contén toda a información necesaria para o seu tratamento: de onde procede e en que estado foi, como cambiaron exactamente os datos, etc. A única cuestión é a viabilidade e a cantidade de información que se pode permitir almacenar.

Como parte do lanzamento da ferramenta de reembolso, utilizamos a terceira opción. Este procesamento de eventos simplificado xa que non había necesidade de extraer información detallada, ademais de eliminar o escenario no que cada novo evento xera unha explosión de solicitudes de aclaración dos consumidores.

Servizo de ferramenta de reembolso non cargado, polo que Kafka hai máis gusto da pluma que unha necesidade. Non creo que se o servizo de reembolso se convertese nun proxecto de gran carga, as empresas estarían felices.

Intercambio asíncrono TAL CUAL

Para intercambios asíncronos, o departamento de PHP adoita usar RabbitMQ. Recollemos os datos da solicitude, puxémolos nunha cola e o consumidor do mesmo servizo leunos e enviounos (ou non o enviou). Para a propia API, Lamoda usa activamente Swagger. Deseñamos unha API, describímola en Swagger e xeramos código de cliente e servidor. Tamén usamos un JSON RPC 2.0 lixeiramente mellorado.

Nalgúns lugares úsanse autobuses ESB, algúns viven en activeMQ, pero, en xeral, RabbitMQ - estándar.

Intercambio asíncrono TO BE

Ao deseñar o intercambio a través do bus de eventos, pódese rastrexar unha analoxía. Do mesmo xeito, describimos o intercambio de datos futuro mediante descricións de estruturas de eventos. O formato yaml, tivemos que facer a xeración de código nós mesmos, o xerador crea DTO segundo a especificación e ensina aos clientes e servidores a traballar con eles. Generation vai en dous idiomas - golang e php. Isto axuda a manter as bibliotecas coherentes. O xerador está escrito en golang, polo que recibiu o nome de gogi.

A fonte de eventos en Kafka é algo típico. Hai unha solución da versión empresarial principal de Kafka Confluent, hai nakadi, unha solución dos nosos irmáns de dominio Zalando. O noso motivación para comezar con vainilla Kafka - isto significa deixar libre a solución ata que finalmente decidamos se a usaremos en todas partes, e tamén deixarnos marxe de manobra e melloras: queremos apoio para o noso JSON RPC 2.0, xeradores para dous idiomas e a ver que máis.

É irónico que mesmo nun caso tan feliz, cando hai un negocio máis ou menos semellante, Zalando, que fixo unha solución máis ou menos semellante, non poidamos utilizalo de forma eficaz.

O patrón arquitectónico no lanzamento é o seguinte: lemos directamente de Kafka, pero escribimos só a través do bus de eventos. Hai moito listo para ler en Kafka: corretores, equilibradores, e está máis ou menos listo para a escala horizontal, quería manter isto. Queriamos completar a gravación mediante un Gateway, tamén coñecido como Events-bus, e aquí tes por que.

Eventos-bus

Ou un autobús de eventos. Esta é simplemente unha pasarela http sen estado, que asume varias funcións importantes:

  • Producindo Validación — comprobamos que os eventos cumpren as nosas especificacións.
  • Sistema principal de eventos, é dicir, este é o sistema principal e único na empresa que responde á pregunta de que eventos con que estruturas se consideran válidas. A validación simplemente implica tipos de datos e enumeracións para especificar estrictamente o contido.
  • Función hash para a fragmentación: a estrutura da mensaxe de Kafka é clave-valor e usando o hash da chave calcúlase onde colocala.

¿Por que

Traballamos nunha gran empresa cun proceso racionalizado. Por que cambiar algo? Este é un experimento, e esperamos obter varios beneficios.

Intercambios 1:n+1 (un a moitos)

Kafka facilita moito conectar novos consumidores á API.

Digamos que tes un directorio que necesitas manter actualizado en varios sistemas á vez (e nalgúns novos). Anteriormente, inventamos un paquete que implementaba set-API e o sistema principal foi informado dos enderezos dos consumidores. Agora o sistema mestre envía actualizacións sobre o tema e todos os que estean interesados ​​leo. Apareceu un novo sistema: rexistrámolo para o tema. Si, tamén paquete, pero máis sinxelo.

No caso da ferramenta de reembolso, que é unha peza de BOB, é conveniente que os mantemos sincronizados a través de Kafka. O pagamento di que o diñeiro foi devolto: BOB, RT decatáronse diso, cambiaron o seu estado, o Servizo de Fiscalización decatouse diso e emitiu un cheque.

Experiencia no desenvolvemento do servizo Refund Tool cunha API asíncrona en Kafka

Temos plans para crear un servizo de notificacións unificado que notificará ao cliente as novidades relativas ao seu pedido/devolucións. Agora esta responsabilidade está repartida entre os sistemas. Abondará con ensinar ao Servizo de notificacións a captar información relevante de Kafka e responder a ela (e desactivar estas notificacións noutros sistemas). Non serán necesarios novos intercambios directos.

Baseado en datos

A información entre os sistemas vólvese transparente, sen importar que "empresa sangrienta" teñas e por moi gordo que sexa o teu atraso. Lamoda conta cun departamento de Análise de Datos que recolle os datos dos sistemas e os pon en forma reutilizable, tanto para empresas como para sistemas intelixentes. Kafka permítelle darlles rapidamente moitos datos e manter ese fluxo de información actualizado.

Rexistro de replicación

As mensaxes non desaparecen despois de ser lidas, como en RabbitMQ. Cando un evento contén información suficiente para procesala, temos un historial de cambios recentes no obxecto e, se o desexa, a capacidade de aplicar estes cambios.

O período de almacenamento do rexistro de replicación depende da intensidade da escritura sobre este tema; Kafka permítelle establecer límites de forma flexible no tempo de almacenamento e no volume de datos. Para temas intensivos, é importante que todos os consumidores teñan tempo para ler a información antes de que desapareza, mesmo no caso de inoperabilidade a curto prazo. Normalmente é posible almacenar datos para unidades de días, que é suficiente para o apoio.

Experiencia no desenvolvemento do servizo Refund Tool cunha API asíncrona en Kafka

A continuación, un pequeno recuento da documentación, para aqueles que non estean familiarizados con Kafka (a imaxe tamén é da documentación)

AMQP ten filas: escribimos mensaxes nunha fila para o consumidor. Normalmente, unha cola é procesada por un sistema coa mesma lóxica empresarial. Se precisas notificar varios sistemas, podes ensinarlle á aplicación a escribir en varias filas ou configurar o intercambio co mecanismo de fanout, que os clona por si mesmo.

Kafka ten unha abstracción semellante tema, na que escribes mensaxes, pero non desaparecen despois de ler. Por defecto, cando te conectas a Kafka, recibes todas as mensaxes e tes a opción de gardar onde o deixaches. É dicir, que le de forma secuencial, non pode marcar a mensaxe como lida, senón que garda o ID desde o que pode seguir lendo. O Id que estableceches chámase compensación e o mecanismo é compensación de commit.

En consecuencia, pódense implementar diferentes lóxicas. Por exemplo, temos BOB en 4 casos para diferentes países: Lamoda está en Rusia, Casaquistán, Ucraína e Bielorrusia. Dado que se despregan por separado, teñen configuracións lixeiramente diferentes e a súa propia lóxica empresarial. Indicamos na mensaxe a que país se refire. Cada consumidor de BOB de cada país le cun groupId diferente e, se a mensaxe non lle corresponde, sáltaa, é dicir. inmediatamente comete offset +1. Se o noso servizo de pagos le o mesmo tema, faino cun grupo separado e, polo tanto, as compensacións non se cruzan.

Requisitos do evento:

  • Completar os datos. Gustaríame que o evento tivese datos suficientes para poder ser procesado.

  • Integridade. Delegamos en Events-bus a verificación de que o evento é consistente e pode procesalo.
  • A orde é importante. No caso dun retorno, vémonos obrigados a traballar coa historia. Coas notificacións, o pedido non é importante, se son notificacións homoxéneas, o correo electrónico será o mesmo independentemente de que pedido chegou primeiro. No caso dun reembolso, hai un proceso claro; se cambiamos a orde, xurdirán excepcións, o reembolso non se creará nin se procesará; acabaremos nun estado diferente.
  • Consistencia. Temos unha tenda e agora creamos eventos en lugar dunha API. Necesitamos un xeito de transmitir de forma rápida e barata información sobre novos eventos e cambios dos existentes aos nosos servizos. Isto conséguese mediante unha especificación común nun repositorio git e xeradores de código separados. Polo tanto, os clientes e servidores en diferentes servizos están coordinados.

Kafka en Lamoda

Temos tres instalacións de Kafka:

  1. Rexistros;
  2. I+D;
  3. Eventos-bus.

Hoxe só falamos do último punto. En events-bus, non temos instalacións moi grandes: 3 corredores (servidores) e só 27 temas. Como regra xeral, un tema é un proceso. Pero este é un punto sutil, e tocarémolo agora.

Experiencia no desenvolvemento do servizo Refund Tool cunha API asíncrona en Kafka

Arriba está o gráfico rps. O proceso de reembolso está marcado cunha liña turquesa (si, a do eixe X) e a liña rosa é o proceso de actualización do contido.

O catálogo de Lamoda contén millóns de produtos e os datos actualízanse todo o tempo. Algunhas coleccións pasan de moda, lánzanse outras novas para substituílas e aparecen constantemente novos modelos no catálogo. Tentamos prever o que será interesante para os nosos clientes mañá, polo que compramos constantemente cousas novas, fotografámolas e actualizamos a vitrina.

Os picos rosas son actualizacións de produtos, é dicir, cambios nos produtos. Pódese ver que os rapaces sacaron fotos, fixeron fotos, e despois de novo! — cargou un paquete de eventos.

Casos de uso de Lamoda Events

Usamos a arquitectura construída para as seguintes operacións:

  • Seguimento do estado de retorno: chamada á acción e seguimento do estado de todos os sistemas implicados. Pago, estados, fiscalización, notificacións. Aquí probamos o enfoque, fixemos ferramentas, recollemos todos os erros, escribimos documentación e dixémoslles aos nosos compañeiros como usala.
  • Actualizando tarxetas de produtos: configuración, metadatos, características. Un sistema le (que amosa) e varios escriben.
  • Correo electrónico, push e sms: recolleuse o pedido, chegou o pedido, aceptouse a devolución, etc., hai moitos.
  • Stock, renovación de almacén — actualización cuantitativa de artigos, só números: chegada ao almacén, devolución. É necesario que todos os sistemas asociados á reserva de mercadorías funcionen cos datos máis actuais. Actualmente, o sistema de actualización de stocks é bastante complexo; Kafka simplificarao.
  • Análise de Datos (Departamento de I+D), ferramentas de ML, analítica, estatística. Queremos que a información sexa transparente - Kafka é moi axeitado para iso.

Agora a parte máis interesante sobre os grandes golpes e descubrimentos interesantes que ocorreron nos últimos seis meses.

Problemas de deseño

Digamos que queremos facer algo novo, por exemplo, transferir todo o proceso de entrega a Kafka. Agora parte do proceso está implementado en Procesamento de pedidos en BOB. Hai un modelo de estado detrás da transferencia dun pedido ao servizo de entrega, o traslado a un almacén intermedio, etc. Hai todo un monólito, incluso dous, ademais dunha morea de API dedicadas á entrega. Saben moito máis sobre a entrega.

Semellan ser áreas similares, pero o procesamento de pedidos en BOB e o sistema de envío teñen estados diferentes. Por exemplo, algúns servizos de mensaxería non envían estados intermedios, senón só os finais: "entregado" ou "perdido". Outros, pola contra, informan con gran detalle sobre o movemento de mercadorías. Cada un ten as súas propias regras de validación: para algúns, o correo electrónico é válido, o que significa que será procesado; para outros non é válido, pero o pedido aínda se procesará porque hai un número de teléfono para contactar, e alguén dirá que tal pedido non se procesará en absoluto.

Fluxo de datos

No caso de Kafka, xorde a cuestión da organización do fluxo de datos. Esta tarefa consiste en escoller unha estratexia baseada en varios puntos; repasámolos todos.

Nun tema ou noutros diferentes?

Temos unha especificación do evento. En BOB escribimos que hai que entregar tal ou cal, e indicamos: o número de pedido, a súa composición, algúns SKU e códigos de barras, etc. Cando a mercadoría chegue ao almacén, a entrega poderá recibir estados, marcas horarias e todo o que sexa necesario. Pero entón queremos recibir actualizacións sobre estes datos en BOB. Temos un proceso inverso de recepción de datos desde a entrega. É este o mesmo evento? Ou é este un intercambio separado que merece o seu propio tema?

O máis probable é que sexan moi similares, e a tentación de facer un tema non é infundada, porque un tema separado significa consumidores separados, configuracións separadas, unha xeración separada de todo isto. Pero non un feito.

Novo campo ou novo evento?

Pero se usas os mesmos eventos, entón xorde outro problema. Por exemplo, non todos os sistemas de entrega poden xerar o tipo de DTO que pode xerar BOB. Enviámoslles o id, pero non o gardan porque non o necesitan, e dende o punto de vista de iniciar o proceso de bus de eventos, este campo é obrigatorio.

Se introducimos unha regra para o bus de eventos que indica que este campo é obrigatorio, entón vémonos obrigados a establecer regras de validación adicionais no BOB ou no controlador de eventos de inicio. A validación comeza a estenderse por todo o servizo - isto non é moi cómodo.

Outro problema é a tentación do desenvolvemento incremental. Dinos que hai que engadirlle algo ao evento, e quizais, se o pensamos ben, debería ser un evento aparte. Pero no noso esquema, un evento separado é un tema separado. Un tema separado é todo o proceso que describín anteriormente. O desenvolvedor ten a tentación de simplemente engadir outro campo ao esquema JSON e rexeneralo.

No caso das devolucións, chegamos ao caso de acontecementos en medio ano. Tivemos un metaevento chamado actualización de reembolso, que tiña un campo de tipo que describía o que era realmente esta actualización. Por iso, tivemos interruptores "marabillosos" con validadores que nos dixeron como validar este evento con este tipo.

Versionado de eventos

Para validar mensaxes en Kafka podes usar Euro, pero foi necesario deitarse inmediatamente sobre el e usar Confluent. No noso caso, temos que ter coidado coa versión. Non sempre será posible reler as mensaxes do rexistro de replicación porque o modelo "saíu". Basicamente, resulta construír versións para que o modelo sexa compatible cara atrás: por exemplo, facer un campo temporalmente opcional. Se as diferenzas son demasiado fortes, comezamos a escribir nun tema novo e trasladamos os clientes cando rematen de ler o antigo.

Orde de lectura garantida das particións

Os temas dentro de Kafka divídense en particións. Isto non é moi importante mentres estamos deseñando entidades e intercambios, pero é importante á hora de decidir como consumilo e escalalo.

No caso habitual, escribes un tema en Kafka. Por defecto, úsase unha partición e todas as mensaxes deste tema van a ela. E, en consecuencia, o consumidor le estas mensaxes secuencialmente. Digamos que agora necesitamos ampliar o sistema para que as mensaxes sexan lidas por dous consumidores diferentes. Se, por exemplo, estás enviando SMS, podes dicirlle a Kafka que faga unha partición adicional e Kafka comezará a dividir as mensaxes en dúas partes: metade aquí, metade aquí.

Como os divide Kafka? Cada mensaxe ten un corpo (no que almacenamos JSON) e unha clave. Podes engadir unha función hash a esta chave, que determinará a que partición entrará a mensaxe.

No noso caso dos reembolsos, isto é importante, se tomamos dúas particións, entón existe a posibilidade de que un consumidor paralelo procese o segundo evento antes do primeiro e haxa problemas. A función hash garante que as mensaxes coa mesma clave acaben na mesma partición.

Eventos vs comandos

Este é outro problema que atopamos. O evento é un evento determinado: dicimos que algo pasou nalgún lugar (algo_sucedeu), por exemplo, un artigo foi cancelado ou se produciu un reembolso. Se alguén escoita estes eventos, de acordo co "elemento cancelado", crearase a entidade de reembolso e escribirase "o reembolso" nalgún lugar das configuracións.

Pero normalmente, cando deseñas eventos, non queres escribilos en balde: confías no feito de que alguén os lea. Hai unha gran tentación de escribir non algo_sucedido (elemento_cancelado, reembolso_reembolsado), senón algo_debería_facerse. Por exemplo, o artigo está listo para ser devolto.

Por unha banda, suxire como se utilizará o evento. Por outra banda, soa moito menos como un nome de evento normal. Ademais, non está moi lonxe de aquí o comando do_something. Pero non tes ningunha garantía de que alguén lea este evento; e se o le, entón leu con éxito; e se o leches con éxito, entón fixeches algo, e ese algo tivo éxito. No momento en que un evento se converte en facer_algo, a retroalimentación faise necesaria, e iso é un problema.

Experiencia no desenvolvemento do servizo Refund Tool cunha API asíncrona en Kafka

No intercambio asíncrono en RabbitMQ, cando le a mensaxe, vai a http, tes unha resposta, polo menos que a mensaxe foi recibida. Cando escribes a Kafka, hai unha mensaxe que escribiches a Kafka, pero non sabes nada sobre como se procesou.

Polo tanto, no noso caso, tivemos que introducir un evento de resposta e configurar un seguimento para que se se envían tantos eventos, despois de tal ou tal tempo debería chegar o mesmo número de eventos de resposta. Se isto non ocorre, entón algo parece ter salido mal. Por exemplo, se enviamos o evento "item_ready_to_refund", esperamos que se cree un reembolso, que o diñeiro devólvese ao cliente e que se nos envíe o evento "money_refunded". Pero isto non é certo, polo que é necesario un seguimento.

Matices

Hai un problema bastante obvio: se le dun tema secuencialmente, e tes algunha mala mensaxe, o consumidor caerá e ti non irás máis lonxe. Precisas parar a todos os consumidores, comprometa máis a compensación para seguir lendo.

Sabíamos diso, contabamos con el, e aínda así pasou. E isto ocorreu porque o evento era válido dende o punto de vista do bus de eventos, o evento era válido dende o punto de vista do validador de aplicacións, pero non era válido dende o punto de vista de PostgreSQL, porque no noso único sistema MySQL con UNSIGNED INT, e no recén escrito o sistema tiña PostgreSQL só con INT. O seu tamaño é un pouco máis pequeno e a identificación non encaixaba. Symfony morreu cunha excepción. Nós, por suposto, captamos a excepción porque confiamos nela e íamos cometer esta compensación, pero antes queriamos aumentar o contador de problemas, xa que a mensaxe foi procesada sen éxito. Os contadores deste proxecto tamén están na base de datos, e Symfony xa pechou a comunicación coa base de datos, e a segunda excepción matou todo o proceso sen posibilidade de realizar unha compensación.

O servizo deitouse durante algún tempo; afortunadamente, con Kafka isto non está tan mal, porque as mensaxes permanecen. Cando se restableza o traballo, podes rematar de lelos. É cómodo.

Kafka ten a capacidade de establecer unha compensación arbitraria mediante ferramentas. Pero para iso, cómpre deter a todos os consumidores; no noso caso, prepare un lanzamento separado no que non haberá consumidores, redistribucións. A continuación, en Kafka pode cambiar a compensación a través de ferramentas e a mensaxe pasará.

Outro matiz - rexistro de replicación vs rdkafka.so - está relacionado coas especificidades do noso proxecto. Usamos PHP, e en PHP, por regra xeral, todas as bibliotecas comunícanse con Kafka a través do repositorio rdkafka.so, e despois hai algún tipo de envoltorio. Quizais estas sexan as nosas dificultades persoais, pero resultou que simplemente reler un anaco do que xa leramos non é tan sinxelo. En xeral, houbo problemas de software.

Volvendo aos detalles específicos de traballar con particións, está escrito directamente na documentación consumidores >= particións de temas. Pero decateino moito máis tarde do que me gustaría. Se queres escalar e ter dous consumidores, necesitas polo menos dúas particións. É dicir, se tivese unha partición na que se acumularan 20 mil mensaxes e fixera unha nova, o número de mensaxes non se igualará en breve. Polo tanto, para ter dous consumidores paralelos, cómpre xestionar particións.

Seguimento

Creo que a forma en que o controlamos será aínda máis claro cales son os problemas que hai no enfoque existente.

Por exemplo, calculamos cantos produtos na base de datos cambiaron recentemente o seu estado e, en consecuencia, deberían ter ocorredo eventos en función destes cambios, e enviamos este número ao noso sistema de seguimento. Despois de Kafka obtemos o segundo número, cantos acontecementos se rexistraron realmente. Obviamente, a diferenza entre estes dous números sempre debe ser cero.

Experiencia no desenvolvemento do servizo Refund Tool cunha API asíncrona en Kafka

Ademais, cómpre supervisar como está o produtor, se os eventos-bus recibiu mensaxes e como o está facendo o consumidor. Por exemplo, nos gráficos a continuación, a Ferramenta de reembolso está a facer ben, pero BOB claramente ten algúns problemas (picos azuis).

Experiencia no desenvolvemento do servizo Refund Tool cunha API asíncrona en Kafka

Xa mencionei o atraso do grupo de consumidores. En liñas xerais, este é o número de mensaxes sen ler. En xeral, os nosos consumidores traballan rapidamente, polo que o desfase adoita ser 0, pero ás veces pode haber un pico a curto prazo. Kafka pode facelo fóra da caixa, pero cómpre establecer un determinado intervalo.

Hai un proxecto Burrowque che dará máis información sobre Kafka. Simplemente usa a API do grupo de consumidores para dar o estado de como está a facer este grupo. Ademais de OK e Failed, hai un aviso e podes descubrir que os teus consumidores non poden soportar o ritmo de produción: non teñen tempo para revisar o escrito. O sistema é bastante intelixente e fácil de usar.

Experiencia no desenvolvemento do servizo Refund Tool cunha API asíncrona en Kafka

Este é o aspecto da resposta da API. Aquí está o grupo bob-live-fifa, partición refund.update.v1, estado OK, desfase 0: o último compensado final tal e tal.

Experiencia no desenvolvemento do servizo Refund Tool cunha API asíncrona en Kafka

Seguimento updated_at SLA (atascado) Xa o mencionei. Por exemplo, o produto cambiou ao estado de que está listo para devolver. Instalamos Cron, que di que se en 5 minutos este obxecto non foi reembolsado (devolvemos o diñeiro a través dos sistemas de pago moi rápido), entón algo definitivamente saíu mal, e definitivamente este é un caso de soporte. Polo tanto, simplemente tomamos Cron, que le tales cousas, e se son maiores que 0, entón envía unha alerta.

Para resumir, usar eventos é conveniente cando:

  • varios sistemas necesitan información;
  • o resultado do procesamento non é importante;
  • hai poucos eventos ou pequenos eventos.

Parece que o artigo ten un tema moi específico: API asíncrona en Kafka, pero en relación con el gustaríame recomendar moitas cousas á vez.
Primeiro, seguinte HighLoad++ hai que esperar ata novembro, en abril haberá unha versión en San Petersburgo e en xuño falaremos de cargas elevadas en Novosibirsk.
En segundo lugar, o autor do informe, Sergei Zaika, é membro do Comité do Programa da nosa nova conferencia sobre xestión do coñecemento. KnowledgeConf. A xornada é dun día, terá lugar o 26 de abril, pero o seu programa é moi intenso.
E será en maio PHP Rusia и RIT++ (con DevOpsConf incluído) - tamén podes propoñer alí o teu tema, falar da túa experiencia e queixarte dos teus conos recheos.

Fonte: www.habr.com

Engadir un comentario