Antipattern: reutilize uma política de quotas

Está a ver a documentação do Apigee e do Apigee Hybrid.
Ver documentação do Apigee Edge.

O Apigee permite configurar o número de pedidos permitidos para um proxy de API durante um período específico através da política de quotas.

Antipattern

Se uma política de quotas for reutilizada, o contador de quotas é decrementado sempre que a política de quotas é executada, independentemente do local onde é usada. Ou seja, se uma política de quotas for reutilizada:

  • Dentro do mesmo fluxo ou de fluxos diferentes de um proxy de API
  • Em diferentes pontos finais de destino de um proxy de API

Em seguida, o contador de quotas é decrementado sempre que é executado e acabamos por receber erros de violação de quotas muito antes do esperado para o intervalo de tempo especificado.

Vamos usar o seguinte exemplo para explicar como funciona.

API proxy

Suponhamos que temos um proxy de API denominado "TestTargetServerQuota", que encaminha o tráfego para dois servidores de destino diferentes com base no caminho do recurso. Além disso, gostaríamos de restringir o tráfego da API a 10 pedidos por minuto para cada um destes servidores de destino. Segue-se a tabela que representa este cenário:

Caminho do recurso Servidor de destino Quota
/target-us target-US.somedomain.com 10 pedidos por minuto
/target-eu target-EU.somedomain.com 10 pedidos por minuto

Política de quotas

Uma vez que a quota de tráfego é a mesma para ambos os servidores de destino, definimos uma única política de quotas denominada "Quota-Minute-Target-Server", conforme mostrado abaixo:

<!-- /antipatterns/examples/1-8.xml -->
<Quota name="Quota-Minute-Target-Server">
  <Interval>1</Interval>
  <TimeUnit>minute</TimeUnit>
  <Distributed>true</Distributed>
  <Allow count="10"/>
</Quota>

Pontos finais de destino

Vamos usar a política de quotas "Quota-Minute-Target-Server" no preflow do ponto final de destino "Target-US":

<!-- /antipatterns/examples/1-9.xml -->
<TargetEndpoint name="Target-US">
  <PreFlow name="PreFlow">
    <Request>
      <Step>
        <Name>Quota-Minute-Target-Server</Name>
      </Step>
    </Request>
  </PreFlow>
  <HTTPTargetConnection>
    <URL>http://target-us.somedomain.com</URL>
  </HTTPTargetConnection>
</TargetEndpoint>

E reutilize a mesma política de quotas "Quota-Minute-Target-Server" no fluxo prévio do outro ponto final "Target-EU" também:

<!-- /antipatterns/examples/1-10.xml -->
<TargetEndpoint name="Target-EU">
  <PreFlow name="PreFlow">
    <Request>
      <Step>
        <Name>Quota-Minute-Target-Server</Name>
      </Step>
    </Request>
  <Response/>
  </PreFlow>
  <HTTPTargetConnection>
    <URL>http://target-us.somedomain.com</URL>
  </HTTPTargetConnection>
</TargetEndpoint>

Padrão de tráfego recebido

Suponhamos que recebemos um total de 10 pedidos de API para este proxy de API nos primeiros 30 segundos no seguinte padrão:

Caminho do recurso /target-us /target-eu Tudo
# Requests 4 6 10

Um pouco mais tarde, recebemos o 11.º pedido de API com o caminho do recurso como /target-us, digamos, após 32 segundos.

Esperamos que o pedido seja processado com êxito, assumindo que ainda temos 6 pedidos de API para o ponto final de destino target-us, de acordo com a quota permitida.

No entanto, na realidade, recebemos um Quota violation error.

Motivo: uma vez que estamos a usar a mesma política de quotas em ambos os pontos finais de destino, é usado um contador de quotas único para acompanhar os pedidos de API que atingem ambos os pontos finais de destino. Assim, esgotamos a quota de 10 pedidos por minuto coletivamente, em vez de para o ponto final de destino individual.

Impacto

Este antipadrão pode resultar numa incompatibilidade fundamental de expetativas, o que leva à perceção de que os limites de quota se esgotaram antes do tempo.

Prática recomendada

  • Use os elementos <Class> ou <Identifier> para garantir que são mantidos vários contadores únicos definindo uma única política de quotas. Vamos redefinir a política de quotas "Quota-Minute-Target-Server" que acabámos de explicar na secção anterior usando o cabeçalho target_id como o <Identifier>, conforme mostrado abaixo:
    <!-- /antipatterns/examples/1-11.xml -->
    <Quota name="Quota-Minute-Target-Server">
      <Interval>1</Interval>
      <TimeUnit>minute</TimeUnit>
      <Allow count="10"/>
      <Identifier ref="request.header.target_id"/>
      <Distributed>true</Distributed>
    </Quota>
    • Vamos continuar a usar esta política de quotas nos pontos finais de destino "Target-US" e "Target-EU" como antes.
    • Agora, suponhamos que, se o cabeçalho target_id tiver o valor "US", os pedidos são encaminhados para o ponto final de destino "Target-US".
    • Da mesma forma, se o cabeçalho target_id tiver o valor "EU", os pedidos são encaminhados para o ponto final de destino "Target-EU".
    • Assim, mesmo que usemos a mesma política de quotas nos dois pontos finais de destino, são mantidos contadores de quotas separados com base no valor <Identifier>.
    • Por conseguinte, ao usar o elemento <Identifier>, podemos garantir que cada um dos pontos finais de destino recebe a quota permitida de 10 pedidos.
  • Use uma política de quotas separada em cada um dos fluxos/pontos finais de destino/proxies de API para garantir que obtém sempre a quantidade permitida de pedidos API. Vejamos agora o mesmo exemplo usado na secção acima para ver como podemos alcançar a quota permitida de 10 pedidos para cada um dos pontos finais de destino.
    • Defina uma política de quotas separada, uma para cada um dos pontos finais de destino "Target-US" e "Target-EU"

      Política de quotas para o ponto final de destino "Target-US":

      <!-- /antipatterns/examples/1-12.xml -->
      <Quota name="Quota-Minute-Target-Server-US">
        <Interval>1</Interval>
        <TimeUnit>minute</TimeUnit>
        <Distributed>true</Distributed>
        <Allow count="10"/>
      </Quota>

      Política de quotas para o ponto final de destino "Target-EU":

      <!-- /antipatterns/examples/1-13.xml -->
      <Quota name="Quota-Minute-Target-Server-EU">
        <Interval>1</Interval>
        <TimeUnit>minute</TimeUnit>
        <Distributed>true</Distributed>
        <Allow count="10"/>
      </Quota>
    • Use a política de quotas respetiva na definição dos pontos finais de destino, conforme mostrado abaixo:

      Ponto final de destino "Target-US":

      <!-- /antipatterns/examples/1-14.xml -->
      <TargetEndpoint name="Target-US">
        <PreFlow name="PreFlow">
          <Request>
            <Step>
              <Name>Quota-Minute-Target-Server-US</Name>
            </Step>
          </Request>
          <Response/>
        </PreFlow>
        <HTTPTargetConnection>
          <URL>http://target-us.somedomain.com</URL>
        </HTTPTargetConnection>
      </TargetEndpoint>

      Ponto final de destino "Target-EU":

      <!-- /antipatterns/examples/1-15.xml -->
      <TargetEndpoint name="Target-EU">
        <PreFlow name="PreFlow">
          <Request>
            <Step>
              <Name>Quota-Minute-Target-Server-EU</Name>
            </Step>
          </Request>
          <Response/>
        </PreFlow>
        <HTTPTargetConnection>
          <URL>http://target-eu.somedomain.com</URL>
        </HTTPTargetConnection>
      </TargetEndpoint>
    • Uma vez que estamos a usar uma política de quotas separada nos pontos finais de destino "Target-US" e "Target-EU", é mantido um contador separado. Isto garante que recebemos a quota permitida de 10 pedidos da API por minuto para cada um dos pontos finais de destino.
  • Use os elementos <Class> ou <Identifier> para garantir a manutenção de vários contadores únicos.