A arquitetura do serviço de fila de mensagens distribuídas no Yandex.Cloud

Olá, meu nome é Vasily Bogonatov. Sou um daqueles que colocam minha mão e cabeça e colocam minha alma a serviço das filas de mensagens persistentes distribuídas da Fila de Mensagens Yandex. O serviço tornou-se público no final de maio, mas no Yandex há muito tempo é usado ativamente em vários produtos.

Hoje, quero contar aos leitores da Habr sobre filas de mensagens em geral e sobre a Fila de Mensagens Yandex em particular. Primeiro, quero explicar o que é uma "fila de mensagens persistentes distribuídas" e por que é necessária. Mostre seu valor prático, a mecânica de trabalhar com mensagens, fale sobre a API e a usabilidade. Na segunda metade do artigo, veremos o lado técnico: como o Yandex Database é usado em nossas filas (essa é uma base confiável do nosso serviço), como é uma abordagem ingênua e aprimorada para a construção de uma arquitetura, quais problemas são causados ​​pela distribuição e como eles podem ser resolvidos.



O que é uma fila de mensagens persistentes distribuída?


A Wikipedia define a fila de mensagens como "um componente de engenharia de software usado para interação entre processos ou entre processos dentro de um processo". De fato, esse conceito é um pouco mais amplo: os processos que interagem usando uma fila podem estar localizados em diferentes servidores e até em diferentes datacenters.

Vamos esclarecer um pouco os termos.

Uma fila de mensagens é um repositório que fornece a localização e a leitura de dados em uma ordem específica.

Dois tipos de entidades geralmente interagem com uma fila:

  • escritores (produtores) - envia mensagens para a fila;
  • leitores (consumidores) - recebe (lê) mensagens da fila.

Ao usar a fila, leitores e escritores são independentes um do outro. Eles podem trabalhar com desempenho, confiabilidade, disponibilidade diferentes e podem até ser escritos em diferentes linguagens de programação.

O cenário principal da fila: transmita de forma confiável e rápida as mensagens do gravador para o leitor. Diferentemente do banco de dados, a fila não se destina ao armazenamento de mensagens a longo prazo. Em muitas implementações populares, existe um parâmetro correspondente - "Período de retenção de mensagens". Determina quanto tempo a mensagem é armazenada até ser excluída permanentemente.

Nós descobrimos o conceito de fila, vá para "distribuição" e "persistência".

  • A distribuição no nosso caso significa a presença de um cluster que armazena e processa dados e metadados da fila, combinando todos os seus nós em um único todo usando uma rede de computadores.
  • A persistência implica que todas as mensagens na fila são gravadas no disco e o gravador recebe a confirmação do envio somente após a gravação bem-sucedida.

A distribuição e a persistência não afetam a funcionalidade principal da fila, elas fornecem tolerância a falhas e confiabilidade no armazenamento de dados. Que tipos de falhas podem ocorrer em nosso sistema, consideraremos um pouco mais tarde. No entanto, não posso negar a mim mesmo o prazer e abrir um pouco os cartões: em toda a história da existência do serviço, não perdemos uma única mensagem salva do cliente.

Para que serve a fila de mensagens?


A fila permite separar as partes de serviços logicamente independentes umas das outras, ou seja, fornece dissociação , o que é tão procurado nos microsserviços agora populares. Isso aumenta a escalabilidade e a confiabilidade: você sempre pode aumentar o fluxo de gravações na fila e adicionar mais leitores - manipuladores de mensagens, enquanto a falha dos leitores não afeta o trabalho dos escritores.

As filas suavizam as cargas de pico: elas atuam como buffers para os leitores. Se as capacidades atuais do leitor não forem suficientes para o processamento instantâneo de todas as mensagens recebidas, as mensagens na fila serão processadas posteriormente quando a carga diminuir. O buffer é útil para serviços com carga instável, em que eventos instantâneos não são necessários.

Vamos ver como funciona, usando o exemplo de um robô de pesquisa (afinal, o Yandex começou com uma pesquisa!), Que baixa, processa e coloca páginas da Web em um banco de dados. Vamos pegar essa arquitetura.



A fila de mensagens resolve os seguintes problemas aqui:

  1. O robô trabalha muito mais rápido do que os trabalhadores responsáveis ​​por analisar e carregar páginas no banco de dados. Fora de linha, os links acumulariam e encheriam a memória ou o disco disponível. O mesmo aconteceria se os trabalhadores estivessem temporariamente indisponíveis.
  2. Sem uma fila, o robô precisa "conhecer" a interface de trabalho dos trabalhadores para atribuir tarefas a eles. A interface pode mudar à medida que o produto se desenvolve.
  3. Um trabalhador individual tem uma confiabilidade bastante baixa, portanto, não há garantia de que o link transmitido seja processado por ele completamente.

A fila fornece armazenamento de dados confiável com escala, permite atrasar o processamento de links. Se um trabalhador falhar, o link bruto após um certo período de tempo será retornado à fila para processamento por outro trabalhador. A fila possui sua própria interface, que é testada e descrita na documentação, para que os sistemas de robô e trabalhador de pesquisa possam desenvolver equipes diferentes em diferentes linguagens de programação. Isso não afetará o desempenho geral.

Como a fila de mensagens Yandex funciona com mensagens


Três estágios principais podem ser distinguidos aqui:

  • escrevendo uma mensagem na fila;
  • lendo uma mensagem da fila;
  • Removendo uma mensagem da fila.

Um registro é considerado bem-sucedido se a mensagem tiver sido armazenada com segurança e em breve estará disponível para os leitores. A gravação de redução de redundância é possível: quando uma tentativa repetida de gravar uma mensagem enviada é ignorada.

No momento da leitura, a mensagem fica oculta da fila por um período chamado Tempo Limite de Visibilidade e fica inacessível para outros leitores. Se o tempo limite da visibilidade expirar, a mensagem retornará à fila e ficará disponível para processamento novamente. A ordem na qual as mensagens são lidas é determinada pela fila, não pelo leitor.

O próprio leitor e a conexão de rede com ele são potencialmente não confiáveis. É necessário um tempo limite de visibilidade para poder retornar uma mensagem para a fila quando o leitor trava ou a conexão é desconectada. Caso contrário, é provável que uma única mensagem nunca seja processada corretamente.

Após a leitura bem-sucedida, a mensagem é transmitida ao cliente com o identificador ReceiptHandle. Um identificador indica dados específicos que devem ser removidos da fila de mensagens.

Tipos de fila na fila de mensagens Yandex


O primeiro e o tipo mais comumente usado é a fila padrão. É caracterizada por alta taxa de transferência (milhares de mensagens por segundo), excelente desempenho e curto tempo de execução das operações básicas. As filas padrão consistem em fragmentos lógicos e suportam o dimensionamento quase linear da largura de banda.


As filas padrão não oferecem suporte à deduplicação de mensagens ao gravar em uma fila e não garantem a ordem de leitura. Devido ao uso de sharding, uma solicitação de leitura pode não retornar uma única mensagem, mesmo se elas estiverem na fila. Na maioria das vezes, isso acontece no modo de pesquisa curta , quando a leitura vem de um fragmento selecionado aleatoriamente.

O segundo tipo - FIFO - é o oposto da fila padrão. Ele fornece uma ordem de leitura rigorosa, suporta desduplicação ao escrever e tentativas repetidas de ler mensagens. O desempenho e a escalabilidade são inferiores ao padrão. O desempenho da fila FIFO é limitado a 30 solicitações por segundo. Recomenda-se que o FIFO seja usado quando você precisar garantir a semântica de entrega "exatamente uma vez". Normalmente, a palavra "fila" significa FIFO.


API da fila de mensagens Yandex


API é um componente extremamente importante de qualquer produto. Uma boa interface de software deve ser simples e direta, exigindo familiarização mínima com a documentação para uso efetivo. Não deve permitir ações estranhas ou desnecessárias e se proteger contra erros estúpidos, informando oportunamente a violação do “contrato”.

Se o sistema tiver uma API, ele recebe rapidamente usuários fiéis e é cercado por "wrappers" convenientes para diferentes plataformas e linguagens de programação.

A API do Amazon Simple Queue Service (API do AWS SQS) é um exemplo dessa interface, testada pelo tempo e por um grande número de clientes. Portanto, decidimos não inventar uma interface exclusiva para a fila de mensagens Yandex, mas implementamos o suporte para a API do AWS SQS e com muito cuidado.

Na maioria dos casos, é suficiente para o usuário do SQS alterar o terminal (endereço de serviço), a região (no momento em que usamos apenas "ru-central1") e obter novas credenciais no Yandex.Cloud. Tudo o resto, por exemplo, um script usando a linha de comando da AWS , código usando o AWS SDK ou um serviço pronto no Celery ou boto , provavelmente não será tocado. A lógica e a funcionalidade do serviço de fila permanecerão as mesmas.


Uma descrição detalhada dos métodos da API da Fila de mensagens Yandex está na documentação do serviço .

Um pouco sobre conveniência


A fila de mensagens Yandex é um serviço gerenciado, ou seja, Yandex, que é responsável pela operação de servidores e software. A equipe de serviço monitora a integridade das filas, substitui rapidamente os discos com falha, elimina quebras de rede e lança atualizações. A atualização não interrompe o serviço: enquanto instalamos a nova versão do YMQ em um grupo de servidores, o balanceador de carga redireciona cuidadosamente o tráfego para outros. Portanto, os usuários não percebem nada.

Para tornar mais conveniente o controle da operação das filas, adicionamos um grande número de gráficos visuais ao YMQ, apenas uma pequena parte deles é mostrada aqui. Os gráficos estão localizados no console do Yandex.Cloud, na seção "Estatísticas".


Falaremos sobre os quatro gráficos mais úteis em nossa opinião:

  • O gráfico "Mensagens em fila" ajuda a monitorar o acúmulo de dados na fila. Um crescimento no gráfico pode significar que os manipuladores não estão gerenciando a carga ou o processamento foi interrompido.
  • Gráfico "Idade da mensagem mais antiga da fila" : valores grandes indicam problemas no processamento da mensagem. Se tudo funcionar corretamente, as mensagens não devem ficar na fila por muito tempo.
  • O gráfico “Número de tentativas para ler uma mensagem” mostra quando as mensagens começam a ser lidas várias vezes. Isso pode significar que os manipuladores falham quando recebem algumas mensagens.
  • O gráfico "Tempo de enfileiramento" indica quanto tempo decorre desde o momento em que a mensagem é enviada para a fila até que o manipulador a receba.

Os gráficos ajudam a avaliar instantaneamente a dinâmica da fila e a presença de falhas sem a necessidade de visualizar logs.

Discutimos pontos mais ou menos gerais, agora vamos aos detalhes.

Como a fila do banco de dados Yandex usa o banco de dados Yandex


O serviço Yandex Message Queue é construído sobre o banco de dados Yandex Database (YDB) tolerante a falhas e distribuído geograficamente , que fornece consistência e suporte rigorosos para transações ACID. Não desmontaremos agora seu dispositivo e suas características, nos limitaremos ao esquema geral.


Uma fila no YMQ consiste em shards lógicos, representados por algum conjunto fixo de tabelas YDB. Cada tabela armazena sua própria informação. Por exemplo, há uma tabela de estado geral chamada State, que armazena offs e o número real de mensagens. Há uma tabela com metadados de dados e mensagens. Há uma tabela com atributos relacionados.

Todas as operações principais com a fila - trabalhando com mensagens, alterando atributos, criando e excluindo - estão funcionando com a hierarquia de tabelas e diretórios YDB ou com consultas transacionais para uma ou mais tabelas da fila. Os dados dentro das tabelas da fila são a fonte da verdade absoluta. Portanto, além da operação correta e estável do banco de dados, é necessário garantir armazenamento confiável e alta disponibilidade de dados.

Nossas informações são armazenadas em várias réplicas: uma cópia em cada um dos três datacenters Yandex. Se um dos data centers não estiver disponível, o número de réplicas nos demais duplicará. Assim, o nível de confiabilidade necessário é restaurado. Mesmo que um data center inteiro e uma central de atendimento em outra falhem, os dados estarão totalmente acessíveis.

A primeira versão da arquitetura da fila de mensagens Yandex


A primeira versão da arquitetura YMQ, que nós mesmos chamamos de ingênua, era assim.


O diagrama mostra o caminho da solicitação HTTPS do cliente YMQ para o repositório YDB. Vejamos os principais componentes:

  1. O balanceador L3 envia uma solicitação ao data center Yandex mais próximo do usuário. Isso reduz a latência da rede, embora a carga seja distribuída de maneira desigual.
  2. O Nginx na máquina virtual Yandex.Cloud encerra as conexões HTTPS, fornece proteção contra ataques à rede e envia a solicitação por proxy ao servidor YMQ, já em HTTP.
  3. O servidor HTTP YMQ implementa a lógica da API HTTP do SQS, valida e converte a solicitação em um formato de protobuf fortemente tipado.
  4. Sistema YMQ Actor - Sistema ator . Ao mesmo tempo, lançou milhares de diferentes atores trocando informações. O sistema de ator de cada host faz parte de um cluster. Todos os atores do cluster vivem e agem como um todo. A lógica de negócios do YMQ é implementada em vários atores envolvidos nas solicitações de transação para o YDB.
  5. Tablets YDB ("tablets") - parte do YDB principal, responsável por trabalhar com tabelas em consultas e transações. Os próprios tablets não armazenam dados. Essas são estruturas de controle na memória que podem restaurar o estado no caso de uma falha de hardware.
  6. O armazenamento é um armazenamento confiável, distribuído e tolerante a falhas.

Essa arquitetura tem uma desvantagem: todos os servidores no cluster trabalham independentemente com tabelas da mesma fila. Isso afeta negativamente o desempenho e impede a organização de caches confiáveis ​​de mensagens ocultas e legíveis. É difícil limitar o fluxo de solicitações, e isso é muito importante para qualquer serviço altamente carregado.

Arquitetura da fila de mensagens Yandex com mestres de filas


O disparo de carga mostrou que a primeira versão da arquitetura suporta cerca de 450 mensagens por segundo por fila com um shard. Era muito pequeno.
O principal problema foram as consultas de contenção. Um grande número de transações logicamente conflitantes levou rapidamente os caches de mensagens ocultas a um estado inconsistente. Para resolver o problema, introduzimos uma entidade especial - o mestre da fila.


O mestre da fila é um ator que, em condições normais, existe em um cluster em uma única instância e passa por si todas as solicitações associadas a uma fila específica. Se uma solicitação para a fila chegar em um servidor em que o mestre desejado está ausente, um agente proxy especial redireciona a solicitação e converte a resposta recebida de volta do mestre.

Ao usar o Assistente de fila, o cache correto de mensagens desbloqueadas reduz a contenção ao trabalhar com tabelas. A implementação da restrição do fluxo de solicitações é simplificada, por exemplo, através do bucket Leaky . Métricas de fila rápidas e precisas estão disponíveis: número de mensagens, tráfego total e similares. Você pode agrupar solicitações semelhantes.

Em teoria, essa arquitetura tem certas desvantagens associadas à centralização:

  1. Reduzindo a tolerância a falhas: se uma máquina virtual com um mestre falhar, todas as filas com mestres estarão indisponíveis. No entanto, os mecanismos especiais do YDB permitem que você crie novos mestres no cluster em apenas alguns segundos. Isso resolve em grande parte o problema.
  2. Escalabilidade limitada: todas as solicitações passam por um host. A desvantagem são os comprimidos YDB nivelados. Eles fazem todo o trabalho duro com os dados. E o mestre envia assincronamente solicitações e processa os resultados recebidos. Isso a torna uma entidade "leve" que não cria um efeito de "gargalo" durante o teste de estresse.

Enfileiramento do Assistente de Consulta


As transações distribuídas com tabelas de banco de dados levam a certos custos adicionais; portanto, a idéia de reduzir o número de consultas nos parecia lógica. Cem transações para gravar mensagens uma de cada vez é melhor se transformar em uma transação para gravar cem mensagens de uma vez. Com mestres de filas, implementar esse processamento em lote (lote) é muito mais fácil.


O lote aumenta ligeiramente a latência durante as operações. Em vez disso, a largura de banda é significativamente aumentada. Com o lote, uma fila de shard único pode processar até 30.000 solicitações por segundo.

Em geral, o carregamento da fila pode ser muito diferente: milhares de mensagens por segundo e várias mensagens por dia. Precisávamos otimizar o trabalho com filas usando um algoritmo flexível. As opções frontais com o acúmulo de mensagens no buffer para o número limite ou uma redefinição do timer não nos convinham. Portanto, desenvolvemos um algoritmo de lote adaptável para o YMQ que funciona bem nos dois casos. Seu trabalho é mostrado em formato de gráfico de tempo.


Aqui, quando uma nova mensagem chega, um dos três cenários é possível:

  1. Uma transação inicia instantaneamente se não houver outras transações desse tipo em execução.
  2. Se já houver transações em execução, a mensagem será adicionada ao buffer e aguardará a conclusão das transações.
  3. Se o tamanho do buffer exceder o valor limite, outra transação paralela será iniciada. O número de transações simultâneas é limitado.

A idéia de lote adaptável se assemelha ao algoritmo Nagle para TCP / IP. : , latency . , . .


Yandex Message Queue, , . , , -.

YDB . YMQ .

, , , .


YMQ . . «» .


YDB . , , , «». , . .


: . , «» . -, «» , «», .

« » . , , .

Yandex Message Queue


Yandex Message Queue – - . . , . .

  • - , , . .
  • API , . , .
  • , : , . , . . boto, 24/7, - .
  • , , . .

. - . . .

:

  • 5, ;
  • YDB;
  • , , , ;
  • , ;
  • . .

.

Em conclusão


– , , , . ., ., ., . .

. . , Yandex Message Queue .

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


All Articles