---
title: "O que torna o OpenResty tão especial - API7.ai"
description: "O OpenResty é construído com NGINX e LuaJIT. Comece por aqui para aprender seus conceitos básicos e como ele funciona."
url: "https://api7.ai/pt/learning-center/openresty/openresty-getting-started"
---

# O que torna o OpenResty tão especial

API7.ai

October 14, 2022

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

Em artigos anteriores, você aprendeu sobre os dois pilares do OpenResty: NGINX e LuaJIT, e tenho certeza de que você está pronto para começar a aprender sobre as APIs que o OpenResty fornece.

Mas não seja muito apressado. Antes de fazer isso, você precisa gastar um pouco mais de tempo se familiarizando com os princípios e conceitos básicos do OpenResty.

## Princípios

![Diagram1](https://static.api7.ai/2022/10/13/634826e45b52e.png?imageMogr2/format/webp)

Os processos `Master` e `Worker` do OpenResty contêm uma VM LuaJIT, que é compartilhada por todas as corrotinas dentro do mesmo processo, e na qual o código Lua é executado.

E em um determinado momento, cada processo `Worker` só pode lidar com solicitações de um usuário, o que significa que apenas uma corrotina está em execução. Você pode ter uma dúvida: Como o NGINX pode suportar C10K (dezenas de milhares de concorrências), ele não precisa lidar com 10.000 solicitações simultaneamente?

Claro que não. O NGINX usa `epoll` para direcionar eventos e reduzir a espera e ociosidade, de modo que o máximo de recursos de CPU possível possa ser usado para processar solicitações de usuários. Afinal, a alta performance só é alcançada quando as solicitações individuais são processadas rapidamente o suficiente. Se um modo multi-thread for usado, onde uma solicitação corresponde a uma thread, então com C10K, os recursos podem ser facilmente esgotados.

No nível do OpenResty, as corrotinas do Lua trabalham em conjunto com o mecanismo de eventos do NGINX. Se uma operação de I/O, como consultar um banco de dados MySQL, ocorrer no código Lua, ela primeiro chamará o `yield` da corrotina Lua para se suspender e, em seguida, registrará um callback no NGINX; após a conclusão da operação de I/O (que também pode ser um timeout ou erro), o callback do NGINX `resume` acordará a corrotina Lua. Isso completa a cooperação entre a concorrência do Lua e os drivers de eventos do NGINX, evitando a escrita de callbacks no código Lua.

Podemos observar o seguinte diagrama, que descreve todo o processo. Tanto `lua_yield` quanto `lua_resume` fazem parte da `lua_CFunction` fornecida pelo Lua.

![Diagram2](https://static.api7.ai/2022/10/13/634828bd1e4fe.png?imageMogr2/format/webp)

Por outro lado, se não houver operações de I/O ou `sleep` no código Lua, como todas as operações intensivas de criptografia e descriptografia, então a VM LuaJIT será ocupada pela corrotina Lua até que toda a solicitação seja processada.

Forneci um trecho do código-fonte de `ngx.sleep` abaixo para ajudá-lo a entender isso mais claramente. Este código está localizado em `ngx_http_lua_sleep.c`, que você pode encontrar no diretório `src` do projeto `lua-nginx-module`.

Em `ngx_http_lua_sleep.c`, podemos ver a implementação concreta da função `sleep`. Você deve primeiro registrar a API Lua `ngx.sleep` com a função C `ngx_http_lua_ngx_sleep`.

```
void ngx_http_lua_inject_sleep_api(lua_State *L)
{
     lua_pushcfunction(L, ngx_http_lua_ngx_sleep);
     lua_setfield(L, -2, "sleep");
}
```

A seguir está a função principal de `sleep`, e eu extraí apenas algumas linhas do código principal aqui.

```
static int ngx_http_lua_ngx_sleep(lua_State *L)
{
    coctx->sleep.handler = ngx_http_lua_sleep_handler;
    ngx_add_timer(&coctx->sleep, (ngx_msec_t) delay);
    return lua_yield(L, 0);
}
```

Como você pode ver:

- Aqui a função de callback `ngx_http_lua_sleep_handler` é adicionada primeiro.
- Em seguida, chama `ngx_add_timer`, uma interface fornecida pelo NGINX, para adicionar um timer ao loop de eventos do NGINX.
- Finalmente, usa `lua_yield` para suspender a concorrência Lua, dando controle ao loop de eventos do NGINX.

A função de callback `ngx_http_lua_sleep_handler` é acionada quando a operação de sleep é concluída. Ela chama `ngx_http_lua_sleep_resume` e, eventualmente, acorda a corrotina Lua usando `lua_resume`. Você pode recuperar os detalhes da chamada no código, então não vou entrar em detalhes aqui.

`ngx.sleep` é apenas o exemplo mais simples, mas ao dissecá-lo, você pode ver os princípios básicos do módulo `lua-nginx-module`.

## Conceitos básicos

Depois de analisar os princípios, vamos refrescar nossa memória e relembrar os dois conceitos importantes de fases e não bloqueio no OpenResty.

O OpenResty, assim como o NGINX, tem o conceito de fases, e cada fase tem seu papel distinto:

- `set_by_lua`, que é usado para definir variáveis.
- `rewrite_by_lua`, para redirecionamento, etc.
- `access_by_lua`, para acesso, permissões, etc.
- `content_by_lua`, para gerar conteúdo de retorno.
- `header_filter_by_lua`, para processamento de filtro de cabeçalho de resposta.
- `body_filter_by_lua`, para filtragem de corpo de resposta.
- `log_by_lua`, para registro.

Claro, se a lógica do seu código não for muito complexa, é possível executá-la toda na fase `rewrite` ou `content`.

No entanto, observe que as APIs do OpenResty têm limites de uso de fase. Cada API tem uma lista de fases em que pode ser usada, e você receberá um erro se usá-la fora do escopo. Isso é muito diferente de outras linguagens de desenvolvimento.

Como exemplo, vou usar `ngx.sleep`. Pela documentação, sei que ele só pode ser usado nos seguintes contextos e não inclui a fase `log`.

```
context: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*_
```

E se você não souber disso e usar `sleep` em uma fase `log` que ele não suporta:

```
location / {
    log_by_lua_block {
        ngx.sleep(1)
     }
}
```

No log de erros do NGINX, há uma indicação de nível `error`.

```
[error] 62666#0: *6 failed to run log_by_lua*: log_by_lua(nginx.conf:14):2: API disabled in the context of log_by_lua*
stack traceback:
    [C]: in function 'sleep'
```

Portanto, antes de usar a API, sempre lembre-se de consultar a documentação para determinar se ela pode ser usada no contexto do seu código.

Depois de revisar o conceito de fases, vamos revisar o não bloqueio. Primeiro, vamos esclarecer que todas as APIs fornecidas pelo OpenResty são não bloqueantes.

Vou continuar com o requisito de **sleep de 1 segundo** como exemplo. Se você quiser implementá-lo em Lua, deve fazer isso.

```
function sleep(s)
   local ntime = os.time() + s
   repeat until os.time() > ntime
end
```

Como o Lua padrão não tem uma função `sleep`, eu uso um loop aqui para continuar verificando se o tempo especificado foi atingido. Essa implementação é bloqueante, e durante o segundo em que `sleep` está em execução, o Lua não faz nada enquanto outras solicitações que precisam ser processadas ficam esperando.

No entanto, se mudarmos para `ngx.sleep(1)`, de acordo com o código-fonte que analisamos acima, o OpenResty ainda pode processar outras solicitações (como `request B`) durante esse segundo. O contexto da solicitação atual (vamos chamá-la de `request A`) será salvo e acordado pelo mecanismo de eventos do NGINX e, em seguida, voltará para `request A`, de modo que a CPU esteja sempre em um estado de trabalho natural.

## Variáveis e ciclo de vida

Além desses dois conceitos importantes, o ciclo de vida das variáveis também é uma área fácil de errar no desenvolvimento do OpenResty.

Como eu disse antes, no OpenResty, recomendo que você declare todas as variáveis como variáveis locais e use ferramentas como `luacheck` e `lua-releng` para detectar variáveis globais. O mesmo vale para módulos, como o seguinte.

```
local ngx_re = require "ngx.re"
```

No OpenResty, exceto para as duas fases `init_by_lua` e `init_worker_by_lua`, uma tabela isolada de variáveis globais é definida para todas as fases para evitar a contaminação de outras solicitações durante o processamento. Mesmo nessas duas fases onde você pode definir variáveis globais, você deve tentar evitar fazer isso.

Como regra, problemas que são tentados a serem resolvidos com variáveis globais devem ser melhor resolvidos com variáveis em módulos e serão muito mais claros. A seguir está um exemplo de uma variável em um módulo.

```
local _M = {}

_M.color = {
      red = 1,
      blue = 2,
      green = 3
  }

  return _M
```

Eu defini um módulo em um arquivo chamado `hello.lua`, que contém a tabela `color`, e então adicionei a seguinte configuração ao `nginx.conf`.

```
location / {
    content_by_lua_block {
        local hello = require "hello"
        ngx.say(hello.color.green)
     }
}
```

Essa configuração exigirá o módulo na fase `content` e imprimirá o valor de `green` como o corpo da resposta HTTP.

Você pode se perguntar por que a variável do módulo é tão incrível?

O módulo só será carregado uma vez no mesmo processo `Worker`; depois disso, todas as solicitações tratadas pelo `Worker` compartilharão os dados no módulo. Dizemos que dados "globais" são adequados para encapsular em módulos porque os `Worker`s do OpenResty são completamente isolados uns dos outros, então cada `Worker` carrega o módulo independentemente, e os dados do módulo não podem cruzar `Worker`s.

Quanto ao tratamento dos dados que precisam ser compartilhados entre `Worker`s, vou deixar isso para um capítulo posterior, então você não precisa se aprofundar nisso aqui.

No entanto, há uma coisa que pode dar errado aqui: ao acessar variáveis de módulo, é melhor mantê-las somente leitura e não tentar modificá-las, ou você terá uma `race` no caso de alta concorrência, um bug que não pode ser detectado por testes unitários, que ocasionalmente ocorre online e é difícil de localizar.

Por exemplo, o valor atual da variável do módulo `green` é `3`, e você faz uma operação de `plus 1` no seu código, então o valor de `green` agora é `4`? Não necessariamente; pode ser `4`, `5` ou `6` porque o OpenResty não bloqueia ao escrever em uma variável de módulo. Então há competição, e o valor da variável do módulo é atualizado por várias solicitações simultaneamente.

Tendo dito isso sobre variáveis globais, locais e de módulo, vamos discutir variáveis entre fases.

Há situações em que precisamos de variáveis que abrangem fases e podem ser lidas e escritas. Variáveis como `$host`, `$scheme`, etc., que são familiares para nós no NGINX, não podem ser criadas dinamicamente, mesmo que satisfaçam a condição de entre fases, e você tem que defini-las no arquivo de configuração antes de poder usá-las. Por exemplo, se você escrever algo como o seguinte.

```
location /foo {
      set $my_var ; # precisa criar a variável $my_var primeiro
      content_by_lua_block {
          ngx.var.my_var = 123
      }
  }
```

O OpenResty fornece `ngx.ctx` para resolver esse tipo de problema. É uma tabela Lua que pode ser usada para armazenar dados Lua baseados em solicitação com o mesmo tempo de vida da solicitação atual. Vamos ver este exemplo da documentação oficial.

```
location /test {
      rewrite_by_lua_block {
          ngx.ctx.foo = 76
      }
      access_by_lua_block {
          ngx.ctx.foo = ngx.ctx.foo + 3
      }
      content_by_lua_block {
          ngx.say(ngx.ctx.foo)
      }
  }
```

Você pode ver que definimos uma variável `foo` que é armazenada em `ngx.ctx`. Essa variável abrange as fases `rewrite`, `access` e `content` e, finalmente, imprime o valor na fase `content`, que é `79`, como esperávamos.

Claro, `ngx.ctx` tem suas limitações.

Por exemplo, solicitações filhas criadas com `ngx.location.capture` terão seus próprios dados `ngx.ctx` separados, independentes do `ngx.ctx` da solicitação pai.

Além disso, redirecionamentos internos criados com `ngx.exec` destroem o `ngx.ctx` da solicitação original e o regeneram com um `ngx.ctx` em branco.

Ambas essas limitações têm [exemplos de código detalhados](https://github.com/openresty/lua-nginx-module#ngxctx) na documentação oficial, então você pode verificá-las se estiver interessado.

## Resumo

Finalmente, vou dizer mais algumas coisas. Estamos aprendendo os princípios do OpenResty e alguns conceitos importantes, mas você não precisa memorizá-los. Afinal, eles sempre fazem sentido e ganham vida quando combinados com requisitos e códigos do mundo real.

Eu me pergunto como você entende isso? Sinta-se à vontade para deixar um comentário e discutir comigo, e também convido você a compartilhar este artigo com seus colegas e amigos. Vamos nos comunicar juntos e progredir juntos.

Share article link

Share

Related Posts

[![E se houver um conflito entre as regras do Lua e a configuração do NGINX?](https://static.api7.ai/2022/10/14/634974e117ebd.png)](https://api7.ai/learning-center/openresty/conflict-between-lua-rules-and-nginx)

## [E se houver um conflito entre as regras do Lua e a configuração do NGINX?](https://api7.ai/learning-center/openresty/conflict-between-lua-rules-and-nginx)

[![Dicas Principais: Identificando Conceitos Únicos e Armadilhas em Lua](https://static.api7.ai/2022/10/14/634974e2dd70a.png)](https://api7.ai/learning-center/openresty/lua-basic-concepts-and-pitfalls)

## [Dicas Principais: Identificando Conceitos Únicos e Armadilhas em Lua](https://api7.ai/learning-center/openresty/lua-basic-concepts-and-pitfalls)

Table of Contents

- [Princípios](#princ%C3%ADpios)
- [Conceitos básicos](#conceitos-b%C3%A1sicos)
- [Variáveis e ciclo de vida](#vari%C3%A1veis-e-ciclo-de-vida)
- [Resumo](#resumo)
