Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Controla a taxa a que a tua aplicação envia pedidos para um serviço, mantendo-te dentro dos limites de limitação e da capacidade global do serviço. Esta abordagem ajuda-o a evitar ou minimizar erros de limitação da taxa e a prever com maior precisão a taxa de transferência.
A limitação de taxa é adequada em muitos cenários, mas é particularmente útil para tarefas automatizadas repetitivas e de grande escala, como o processamento em lote.
Contexto e problema
Executar um grande número de operações num serviço com limitação de débito pode resultar num aumento do tráfego e numa redução da capacidade de processamento, porque é necessário monitorizar os pedidos rejeitados e voltar a tentar as operações. À medida que o número de operações aumenta, um limite de limitação pode exigir múltiplas passagens de reenvio de dados, o que resulta num impacto de desempenho maior.
Por exemplo, considere o seguinte processo problemático de repetição em caso de erro para introduzir dados no Azure Cosmos DB:
Seu aplicativo precisa ingerir 10.000 registros no Azure Cosmos DB. Cada registo custa 10 unidades de pedido (RUs) para ingestão, pelo que são necessárias 100 000 RUs para concluir a tarefa.
Sua instância do Azure Cosmos DB tem 20.000 RUs de capacidade provisionada.
Você envia todos os 10.000 registros para o Azure Cosmos DB. 2.000 registros são gravados com sucesso e 8.000 registros são rejeitados.
Você envia os 8.000 registros restantes para o Azure Cosmos DB. 2.000 registros são escritos com sucesso e 6.000 registros são rejeitados.
Você envia os 6.000 registros restantes para o Azure Cosmos DB. 2.000 registros são escritos com sucesso e 4.000 registros são rejeitados.
Você envia os 4.000 registros restantes para o Azure Cosmos DB. 2.000 registros são gravados com sucesso e 2.000 registros são rejeitados.
Você envia os 2.000 registros restantes para o Azure Cosmos DB. Todos são escritos com sucesso.
O trabalho de ingestão é concluído com sucesso, mas apenas após enviar 30.000 registos para o Azure Cosmos DB. Todo o conjunto de dados consiste em apenas 10.000 registos.
Existem outros fatores a considerar neste exemplo:
Um grande número de erros pode também resultar em trabalho extra para registar esses erros e processar os dados resultantes. A abordagem anterior lida com 20.000 erros, e registar esses erros pode impor um custo de processamento, memória ou recursos de armazenamento.
Como não conheces os limites de limitação do serviço de ingestão, não tens forma de definir expectativas sobre quanto tempo demora o processamento de dados. A limitação da taxa pode permitir calcular o tempo necessário para a ingestão.
Solução
A limitação da taxa pode reduzir o seu tráfego e potencialmente melhorar a taxa de transferência, reduzindo o número de registos enviados para um serviço durante um determinado período de tempo.
Um serviço pode limitar pedidos com base em diferentes métricas ao longo do tempo, tais como:
- O número de operações (por exemplo, 20 solicitações por segundo).
- A quantidade de dados (por exemplo, 2 GiB por minuto).
- O custo relativo das operações (por exemplo, 20.000 RUs por segundo).
Independentemente da métrica que utilize para a limitação de taxa, a sua implementação de limitação de taxa consistirá em controlar o número e/ou a dimensão das operações enviadas para o serviço num determinado período de tempo. A limitação de taxa otimiza a utilização do serviço sem exceder a sua capacidade de limitação.
Em cenários em que as suas APIs conseguem lidar com pedidos mais rapidamente do que os serviços de ingestão limitados permitem, deve gerir a rapidez com que utiliza o serviço. Tratar a limitação apenas como uma incompatibilidade de velocidade de dados e bufferizar os pedidos de ingestão até que o serviço recupere cria um risco. Se a sua aplicação deixar de responder neste cenário, quaisquer dados em buffer podem ser perdidos.
Para evitar esse risco, considere enviar seus registros para um sistema de mensagens durável que possa lidar com sua taxa de ingestão completa. (Serviços como o Hubs de Eventos do Azure podem gerir milhões de operações por segundo.) Pode então usar um ou mais processadores de tarefas para ler os registos do sistema de mensagens a uma taxa controlada dentro dos limites do serviço limitado. Submeter registos ao sistema de mensagens pode poupar memória interna, permitindo-lhe retirar da fila apenas os registos que podem ser processados durante um determinado intervalo de tempo.
O Azure fornece vários serviços de mensagens duráveis que você pode usar com esse padrão, incluindo:
Quando envia registos, o período que utiliza para divulgar registos pode ser mais detalhado do que o período em que o serviço limita. Os sistemas costumam definir limites com base em intervalos de tempo que pode compreender e utilizar facilmente. No entanto, para o computador que executa um serviço, estes prazos podem ser muito longos comparados com a rapidez com que pode processar informação. Por exemplo, um sistema pode acelerar por segundo ou por minuto, mas normalmente o código é processado na ordem de nanossegundos ou milissegundos.
Embora não seja obrigatório, é frequentemente recomendado enviar um número menor de registos com mais frequência para melhorar o débito. Assim, em vez de tentares agrupar registos para libertação uma vez por segundo ou uma vez por minuto, podes fazê-lo de forma mais granular para manter o consumo de recursos (memória, CPU e rede) distribuído de forma mais uniforme. Esta abordagem previne potenciais gargalos causados por surtos súbitos de pedidos. Por exemplo, se um serviço permite 100 operações por segundo, a implementação de um limitador de taxa pode equilibrar os pedidos ao libertar 20 operações a cada 200 milissegundos, como mostrado no gráfico seguinte.
Além disso, às vezes é necessário que vários processos descoordenados compartilhem um serviço limitado. Para implementar limitação de taxa neste cenário, pode-se particionar logicamente a capacidade do serviço e depois usar um sistema distribuído de exclusão mútua para gerir bloqueios exclusivos nessas partições. Os processos descoordenados podem então competir por bloqueios nessas partições sempre que precisarem de capacidade. Para cada partição para a qual um processo mantém um bloqueio, é concedida uma certa quantidade de capacidade.
Por exemplo, se o sistema acelerado permitir 500 solicitações por segundo, você poderá criar 20 partições no valor de 25 solicitações por segundo cada. Se um processo precisar emitir 100 solicitações, ele pode solicitar ao sistema de exclusão mútua distribuído quatro partições. O sistema pode conceder duas partições por 10 segundos. O processo classificaria o limite para 50 solicitações por segundo, concluiria a tarefa em dois segundos e, em seguida, liberaria o bloqueio.
Uma forma de implementar este padrão é usar o Armazenamento do Azure. Nesse cenário, você cria um blob de 0 bytes por partição lógica em um contêiner. As suas aplicações podem então obter bloqueios exclusivos diretamente sobre esses blobs por um curto período de tempo (por exemplo, 15 segundos). Por cada concessão atribuída a uma aplicação, esta pode utilizar a quantidade de capacidade dessa partição. A candidatura precisa então de acompanhar o tempo do arrendamento para que, quando o prazo expirar, a candidatura deixe de usar a capacidade que lhe foi concedida. Quando implementas este padrão, muitas vezes vais querer que cada processo tente alugar uma partição aleatória quando precisar de capacidade.
Para reduzir ainda mais a latência, você pode alocar uma pequena quantidade de capacidade exclusiva para cada processo. Nesse caso, um processo só procuraria obter um contrato de locação de capacidade partilhada se fosse necessário exceder a sua capacidade reservada.
Como alternativa ao Armazenamento do Azure, também pode implementar este tipo de sistema de gestão de arrendamentos usando tecnologias como ZooKeeper, etcd, e Redis/Redsync.
Problemas e considerações
Considere os seguintes pontos ao decidir como implementar este padrão:
Embora o padrão de limitação de taxa possa reduzir o número de erros de limitação, a sua aplicação ainda precisa de lidar corretamente com quaisquer erros de limitação que possam ocorrer.
Assegure que as tentativas são coordenadas com limitação de taxas. Retentativas cegas ou excessivamente agressivas podem aumentar a carga e criar tempestades de retentativas, por isso propague sinais de back-pressure (por exemplo, HTTP 429 com
Retry-After) e utilize um número limitado de retentativas com pequenos atrasos aleatórios entre tentativas.Se a sua aplicação tiver vários fluxos de trabalho que acedem ao mesmo serviço limitado, precisa de integrar todos eles na sua estratégia de limitação de taxas. Por exemplo, você pode oferecer suporte ao carregamento em massa de registros em um banco de dados, mas também consultar registros nesse mesmo banco de dados. Pode gerir a capacidade garantindo que todos os fluxos de trabalho são bloqueados pelo mesmo mecanismo de limitação de taxas. Como alternativa, você pode reservar pools separados de capacidade para cada fluxo de trabalho.
O serviço restringido pode ser usado em múltiplas aplicações. Em alguns casos, é possível coordenar essa utilização (como mostrado anteriormente neste artigo). Se começar a ver um número maior de erros de limitação do que o esperado, esse número aumentado pode indicar contenção entre aplicações que acedem a um serviço. Neste caso, poderá ser necessário ponderar reduzir temporariamente a taxa de transferência imposta pelo seu mecanismo de limitação de taxa até que a utilização por parte de outras aplicações diminua.
Quando utilizar este padrão
Utilize este padrão quando:
Precisa de reduzir os erros de limitação causados por um serviço com tarifa limitada.
Pretende minimizar o tráfego, em comparação com abordagens básicas de repetição em caso de erro.
É necessário reduzir o consumo de memória retirando os registos da fila apenas quando houver capacidade suficiente para os poder processar.
Este padrão pode não ser adequado quando:
A operação requer conclusão imediata e síncrona com latência muito baixa e não tolera filas ou processamento diferido.
O principal gargalo não é a taxa de pedidos, mas sim a concorrência ou a contenção de recursos (por exemplo, saturação do CPU ou trabalho prolongado em voo). Nestes casos, controlos de escalabilidade ou concorrência são mais adequados.
Design da carga de trabalho
Avalie de que forma utilizar o padrão de limitação da taxa na conceção de uma carga de trabalho para dar resposta aos objetivos e princípios descritos nos pilares do Azure Well-Architected Framework. A tabela a seguir fornece orientação sobre como esse padrão suporta as metas de cada pilar.
| Pilar | Como esse padrão suporta os objetivos do pilar |
|---|---|
| As decisões de projeto de confiabilidade ajudam sua carga de trabalho a se tornar resiliente ao mau funcionamento e garantem que ela se recupere para um estado totalmente funcional após a ocorrência de uma falha. | Esta tática protege o cliente ao reconhecer e respeitar as limitações e custos de comunicar com um serviço quando este prefere evitar o uso excessivo. - RE:07 Autopreservação |
Se este padrão introduzir compensações dentro de um pilar, considere-as em relação aos objetivos dos outros pilares.
Example
O aplicativo de exemplo a seguir permite que os usuários enviem registros de vários tipos para uma API. Cada tipo de registo tem um processador de tarefas único que executa os seguintes passos:
- Validation
- Enriquecimento
- Inserção do registo na base de dados
Todos os componentes da aplicação (API, processador de tarefas A e processador de tarefas B) são processos separados que podem ser escalados de forma independente. Os processos não comunicam diretamente entre si.
Neste exemplo, cada arrendamento de blob representa uma quota fixa do débito permitido da base de dados. Um processador só pode desalinhar e escrever à taxa combinada dos arrendamentos que atualmente detém. À medida que os processadores ganham ou perdem arrendamentos ao longo do tempo, a taxa de escrita permitida muda, o que mantém o tráfego total da base de dados dentro do limite configurado, permitindo que todo o trabalho em fila progrida.
Este diagrama incorpora o seguinte fluxo de trabalho:
- Um usuário envia 10.000 registros do tipo A para a API.
- A API coloca esses 10.000 registos na fila A.
- Um usuário envia 5.000 registros do tipo B para a API.
- A API coloca esses 5.000 registos na fila B.
- O processador de trabalhos A deteta que a fila A contém registos e tenta obter uma locação exclusiva sobre o blob 2.
- O processador de trabalhos B verifica que a fila B tem registos e tenta obter uma locação exclusiva sobre o blob 2.
- O processador de trabalhos A não consegue obter o contrato de arrendamento.
- O processador de tarefas B adquire o arrendamento do blob 2 durante 15 segundos. Agora, é possível limitar a taxa de pedidos para a base de dados a 100 por segundo.
- O processador de trabalhos B remove 100 registos da fila B e escreve-os.
- Um segundo passa.
- O processador de trabalhos A vê que a fila A tem mais registos e tenta obter um arrendamento exclusivo no blob 6.
- O processador de tarefas B deteta que a fila B tem mais registos e tenta obter uma concessão exclusiva sobre o blob 3.
- O processador de tarefas A adquire a concessão do bloco 6 durante 15 segundos. Agora, é possível limitar a taxa de pedidos para a base de dados a 100 por segundo.
- O processador de tarefas B adquire o arrendamento sobre o blob 3 por 15 segundos. Agora pode limitar a taxa de pedidos para a base de dados a 200 por segundo. (Também detém o contrato de arrendamento do blob 2.)
- O processador de trabalhos A remove 100 registos da fila A e escreve-os.
- O processador de trabalhos B remove 200 registos da fila B e escreve-os.
- Um segundo passa.
- O processador de trabalhos A deteta que a fila A tem mais registos e tenta obter uma locação exclusiva no blob 0.
- O processador de trabalhos B deteta que a fila B tem mais registos e tenta obter uma concessão exclusiva no blob 1.
- O processador de tarefas A obtém a concessão do blob 0 por 15 segundos. Agora pode limitar a taxa de pedidos para a base de dados a 200 por segundo. (É também titular do contrato de arrendamento do blob 6.)
- O processador de tarefas B adquire o arrendamento do blob 1 durante 15 segundos. Agora, pode limitar os pedidos à base de dados a 300 por segundo. (Também detém o contrato de arrendamento para as bolhas 2 e 3.)
- O processador de trabalhos A remove 200 registos da fila A e escreve-os.
- O processador de tarefas B retira 300 registos da fila B e escreve-os.
- E assim por diante.
Após 15 segundos, um ou ambos os trabalhos continuam sem ser concluídos. À medida que os contratos de arrendamento expiram, um processador deve também reduzir o número de pedidos que remove e escreve.
Implementações deste padrão estão disponíveis em diferentes linguagens de programação:
Passos seguintes
As seguintes orientações também podem ser relevantes ao implementar este padrão:
Limitação avançada de pedidos com API Management do Azure. Utilize isto como controlo complementar de admissão na periferia para aplicar limites e quotas de taxa de chamadas por chave, e para devolver sinais consistentes de contrapressão aos clientes.
Escolha entre os serviços de mensagens do Azure. Selecione a melhor infraestrutura base de mensagens duradoura para armazenamento em memória intermédia e ingestão controlada.
Lida com falhas transitórias em aplicações Azure. Conceba o comportamento de retentativa para que os clientes reduzam corretamente a frequência das tentativas quando os limites forem atingidos.
Recursos relacionados
Os seguintes padrões e orientações também podem ser relevantes ao implementar este padrão:
Throttling. O padrão de limitação de taxa é normalmente implementado em resposta a um serviço limitado.
Retry. Quando pedidos a um serviço limitado resultam em erros de limitação, é geralmente apropriado tentar novamente esses pedidos após um intervalo apropriado.
Nivelamento da Carga Baseado em Filas é semelhante ao padrão de limitação da taxa, mas difere em vários aspetos fundamentais:
O limite de taxa não precisa necessariamente usar filas para gerenciar a carga, mas precisa fazer uso de um serviço de mensagens durável. Por exemplo, um padrão de limitação de taxa pode usar serviços como o Apache Kafka ou Event Hubs.
O padrão Rate Limiting introduz o conceito de um sistema distribuído de exclusão mútua nas partições, que permite gerir a capacidade de múltiplos processos não coordenados que comunicam com o mesmo serviço limitado.
Um padrão de nivelamento de carga baseado em filas aplica-se sempre que houver uma discrepância de desempenho entre serviços ou quando se pretende melhorar a resiliência. Portanto, é um padrão mais amplo do que o Limite de Velocidade, que se preocupa mais especificamente com o acesso eficiente a um serviço limitado.