---
title: "Limitação Dinâmica de Taxa no OpenResty - API7.ai"
description: "Vamos aprender como usar a implementação de `limit-req`, `limit-count`, `limit-conn` e `limit.traffic` no `lua-resty-limit-traffic` e realizar a limitação de taxa com eles."
url: "https://api7.ai/pt/learning-center/openresty/flexible-dynamic-rate-limiting"
---

# Limitação Dinâmica de Taxa no OpenResty

API7.ai

January 6, 2023

[OpenResty (NGINX + Lua)](https://api7.ai/learning-center/openresty)

No artigo anterior, apresentei os algoritmos de `leaky bucket` e `token bucket`, que são comuns para lidar com tráfego em rajadas. Além disso, aprendemos como realizar a limitação de taxa de solicitações usando a configuração do NGINX. No entanto, usar a configuração do NGINX está apenas no nível de usabilidade e ainda há um longo caminho para ser útil.

O primeiro problema é que a chave de limitação de taxa é limitada a um intervalo de variáveis do NGINX e não pode ser definida de forma flexível. Por exemplo, não há como definir diferentes limites de velocidade para diferentes províncias e diferentes canais de clientes, o que é uma necessidade comum com o NGINX.

Outro problema maior é que a taxa não pode ser ajustada dinamicamente, e cada alteração requer a recarga do serviço NGINX. Como resultado, limitar a velocidade com base em diferentes períodos só pode ser implementado de forma precária por meio de scripts externos.

É importante entender que a tecnologia serve ao negócio e, ao mesmo tempo, o negócio impulsiona a tecnologia. No momento do nascimento do NGINX, havia pouca necessidade de ajustar a configuração dinamicamente; era mais sobre proxy reverso, balanceamento de carga, baixo uso de memória e outras necessidades semelhantes que impulsionaram o crescimento do NGINX. Em termos de arquitetura e implementação de tecnologia, ninguém poderia ter previsto a enorme explosão de demanda por controle dinâmico e granular em cenários como a Internet móvel, IoT e microsserviços.

O uso de scripts Lua pelo OpenResty compensa a falta do NGINX nessa área, tornando-o um complemento eficaz. É por isso que o OpenResty é tão amplamente usado como substituto do NGINX. Nos próximos artigos, continuarei apresentando mais cenários e exemplos dinâmicos no OpenResty. Vamos começar vendo como usar o OpenResty para implementar a limitação de taxa dinâmica.

No OpenResty, recomendamos o uso de [lua-resty-limit-traffic](https://github.com/openresty/lua-resty-limit-traffic) para limitar o tráfego. Ele inclui `limit-req` (limitar a taxa de solicitações), `limit-count` (limitar o número de solicitações) e `limit-conn` (limitar conexões simultâneas); e fornece `limit.traffic` para agregar esses três métodos.

## Limitar a taxa de solicitações

Vamos começar olhando para o `limit-req`, que usa um algoritmo de leaky bucket para limitar a taxa de solicitações.

Na seção anterior, apresentamos brevemente o código de implementação chave do algoritmo de leaky bucket nesta biblioteca resty, e agora aprenderemos como usar essa biblioteca. Primeiro, vamos ver o seguinte código de exemplo.

```
resty --shdict='my_limit_req_store 100m' -e 'local limit_req = require "resty.limit.req"
local lim, err = limit_req.new("my_limit_req_store", 200, 100)
local delay, err = lim:incoming("key", true)
if not delay then
    if err == "rejected" then
        return ngx.exit(503)
    end
    return ngx.exit(500)
end

 if delay >= 0.001 then
    ngx.sleep(delay)
end'
```

Sabemos que o `lua-resty-limit-traffic` usa um `shared dict` para armazenar e contar chaves, então precisamos declarar o espaço de `100m` para `my_limit_req_store` antes de podermos usar o `limit-req`. Isso é semelhante para `limit-conn` e `limit-count`, que precisam de espaços separados de `shared dict` para serem distinguidos.

```
limit_req.new("my_limit_req_store", 200, 100)
```

A linha de código acima é uma das mais críticas. Ela significa que um `shared dict` chamado `my_limit_req_store` é usado para armazenar estatísticas, e a taxa por segundo é definida como `200`, de modo que, se exceder `200` mas for menor que `300` (este valor é calculado a partir de `200 + 100`), será enfileirado, e se exceder `300`, será rejeitado.

Após a configuração, temos que processar a solicitação do cliente. `lim: incoming("key", true)` está aqui para fazer isso. `incoming` tem dois parâmetros, que precisamos ler em detalhes.

O primeiro parâmetro, a chave especificada pelo usuário para a limitação de taxa, é uma constante de string no exemplo acima, o que significa que a limitação de taxa deve ser uniforme para todos os clientes. Se você quiser limitar a taxa de acordo com diferentes províncias e canais, é muito simples usar ambas as informações como a chave, e o seguinte é o pseudo-código para alcançar esse requisito.

```
local  province = get_ province(ngx.var.binary_remote_addr)
local channel = ngx.req.get_headers()["channel"]
local key = province .. channel
lim:incoming(key, true)
```

Claro, você também pode personalizar o significado da chave e as condições para chamar `incoming`, para que possa obter um efeito muito flexível de limitação de taxa.

Vamos olhar para o segundo parâmetro da função `incoming`, e é um valor booleano. O padrão é `false`, o que significa que a solicitação não será registrada no `shared dict` para estatísticas; é apenas um exercício. Se for definido como true, terá um efeito real. Portanto, na maioria dos casos, você precisará defini-lo explicitamente como true.

Você pode se perguntar por que esse parâmetro existe. Considere um cenário em que você configura duas instâncias diferentes de `limit-req` com chaves diferentes, uma chave sendo o nome do host e a outra sendo o endereço IP do cliente. Então, quando uma solicitação do cliente é processada, os métodos `incoming` dessas duas instâncias são chamados em ordem, conforme indicado pelo seguinte pseudo-código.

```
local limiter_one, err = limit_req.new("my_limit_req_store", 200, 100)
local limiter_two, err = limit_req.new("my_limit_req_store", 20, 10)

limiter_one :incoming(ngx.var.host, true)
limiter_two:incoming(ngx.var.binary_remote_addr, true)
```

Se a solicitação do usuário passar pela detecção de limite de `limiter_one` mas for rejeitada pela detecção de `limiter_two`, então a chamada da função `limiter_one:incoming` deve ser considerada um exercício e não precisamos contá-la.

Nesse caso, a lógica do código acima não é rigorosa o suficiente. Precisamos fazer um exercício de todos os limitadores antecipadamente, para que, se um limite de limitador for acionado que possa rejeitar a solicitação do cliente, ele possa retornar diretamente.

```
for i = 1, n do
    local lim = limiters[i]
    local delay, err = lim:incoming(keys[i], i == n)
    if not delay then
        return nil, err
    end
end
```

É sobre isso que se trata o segundo argumento da função `incoming`. Este código é o código central do módulo `limit.traffic`, que é usado para combinar vários limitadores de taxa.

## Limitar o número de solicitações

Vamos dar uma olhada no `limit.count`, uma biblioteca que limita o número de solicitações. Ela funciona como o GitHub API Rate Limiting, que limita o número de solicitações do usuário em uma janela de tempo fixa. Como de costume, vamos começar com um código de exemplo.

```
local limit_count = require "resty.limit.count"

local lim, err = limit_count.new("my_limit_count_store", 5000, 3600)

local key = ngx.req.get_headers()["Authorization"]
local delay, remaining = lim:incoming(key, true)
```

Você pode ver que `limit.count` e `limit.req` são usados de forma semelhante. Começamos definindo um `shared dict` no `nginx.conf`.

```
lua_shared_dict my_limit_count_store 100m;
```

Então `new` um objeto limitador, e finalmente usamos a função `incoming` para determinar e processar.

No entanto, a diferença é que o segundo valor de retorno da função incoming em `limit-count` representa as chamadas restantes, e podemos adicionar campos ao cabeçalho de resposta para dar uma indicação melhor ao cliente.

```
ngx.header["X-RateLimit-Limit"] = "5000"
ngx.header["X-RateLimit-Remaining"] = remaining
```

## Limitar o número de conexões simultâneas

`limit.conn` é uma biblioteca para limitar o número de conexões simultâneas. Ela difere das duas bibliotecas mencionadas anteriormente por ter uma API especial `leaving`, que descreverei brevemente aqui.

Limitar a taxa de solicitação e o número de solicitações, como mencionado acima, pode ser feito diretamente na fase de `access`. Diferente de limitar o número de conexões simultâneas, que requer não apenas determinar se o limite foi excedido na fase de `access`, mas também chamar a API `leaving` na fase de `log`.

```
log_by_lua_block {
    local latency = tonumber(ngx.var.request_time) - ctx.limit_conn_delay
    local key = ctx.limit_conn_key

    local conn, err = lim:leaving(key, latency)
}
```

No entanto, o código central dessa API é bastante simples, que é a seguinte linha de código que decrementa o número de conexões em um. Se você não limpar na fase de `log`, o número de conexões continuará subindo e logo atingirá o limite de simultaneidade.

```
local conn, err = dict:incr(key, -1)
```

## Combinação de limitadores de taxa

Este é o fim da introdução de cada um desses três métodos. Finalmente, vamos ver como combinar `limit.rate`, `limit.conn` e `limit.count`. Aqui precisamos usar a função `combine` em `limit.`traffic\`.

```
local lim1, err = limit_req.new("my_req_store", 300, 200)
local lim2, err = limit_req.new("my_req_store", 200, 100)
local lim3, err = limit_conn.new("my_conn_store", 1000, 1000, 0.5)

local limiters = {lim1, lim2, lim3}
local host = ngx.var.host
local client = ngx.var.binary_remote_addr
local keys = {host, client, client}

local delay, err = limit_traffic.combine(limiters, keys, states)
```

Este código deve ser fácil de entender com o conhecimento que você acabou de adquirir. O código central da função `combine`, que já mencionamos na análise de `limit.rate` acima, é principalmente implementado com a ajuda da função `drill` e da função `uncommit`. Essa combinação permite que você defina diferentes limites e chaves para vários limitadores para atender a requisitos de negócios mais complexos.

## Resumo

Não apenas o `limit.traffic` suporta os três limitadores de taxa mencionados hoje, mas desde que o limitador de taxa tenha as APIs `incoming` e `uncommit`, ele pode ser gerenciado pela função `combine` do `limit.traffic`.

Finalmente, vou deixar uma tarefa para você. Você pode escrever um exemplo que combine os limitadores de taxa de token e bucket que apresentamos antes? Sinta-se à vontade para escrever sua resposta na seção de comentários para discutir comigo, e você também é bem-vindo a compartilhar este artigo com seus colegas e amigos para aprender e se comunicar juntos.

Share article link

Share

Related Posts

[![FAQ do OpenResty | Como o OpenResty é usado na prática](https://static.api7.ai/uploads/2023/02/12/jbv9M0zi_Hello%20World%20in%20OpenResty%20%283%29.png)](https://api7.ai/learning-center/openresty/faq-5)

## [FAQ do OpenResty | Como o OpenResty é usado na prática](https://api7.ai/learning-center/openresty/faq-5)

[![Parte 3: Como Construir um API Gateway de Microservices Usando OpenResty](https://static.api7.ai/uploads/2023/02/07/3XJXY9Jy_45.png)](https://api7.ai/learning-center/openresty/how-to-build-a-microservices-api-gateway-part-3)

## [Parte 3: Como Construir um API Gateway de Microservices Usando OpenResty](https://api7.ai/learning-center/openresty/how-to-build-a-microservices-api-gateway-part-3)

Table of Contents

- [Limitar a taxa de solicitações](#limitar-a-taxa-de-solicita%C3%A7%C3%B5es)
- [Limitar o número de solicitações](#limitar-o-n%C3%BAmero-de-solicita%C3%A7%C3%B5es)
- [Limitar o número de conexões simultâneas](#limitar-o-n%C3%BAmero-de-conex%C3%B5es-simult%C3%A2neas)
- [Combinação de limitadores de taxa](#combina%C3%A7%C3%A3o-de-limitadores-de-taxa)
- [Resumo](#resumo)
