---
title: "Além do Web Server: Processos Privilegiados e Tarefas de Timer - API7.ai"
description: "Privileged Process e Timer Tasks são recursos importantes que distinguem o OpenResty do NGINX e de outros servidores web."
url: "https://api7.ai/pt/learning-center/openresty/privileged-process-and-timer-tasks"
---

# Além do Web Server: Processos Privilegiados e Tarefas de Timer

API7.ai

November 3, 2022

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

No artigo anterior, apresentamos as APIs do OpenResty, `shared dict` e `cosocket`, que implementam funcionalidades no âmbito do NGINX e servidores web, fornecendo um servidor web programável de menor custo e mais fácil de manter.

No entanto, o OpenResty pode fazer mais do que isso. Vamos destacar alguns recursos do OpenResty que vão além do servidor web e apresentá-los hoje. Eles são tarefas de timer, processo privilegiado e `ngx.pipe` não bloqueante.

## Tarefas de Timer

No OpenResty, às vezes precisamos executar tarefas específicas em segundo plano regularmente, como sincronizar dados, limpar logs, etc. Se você fosse projetar isso, como faria? A maneira mais fácil de pensar é fornecer uma interface API para o exterior para executar essas tarefas, depois usar o `crontab` do sistema para chamar `curl` em intervalos regulares para acessar essa interface e, assim, implementar essa necessidade de forma indireta.

No entanto, isso não apenas seria fragmentado, mas também traria maior complexidade para a operação e manutenção. Portanto, o OpenResty fornece `ngx.timer` para resolver esse tipo de necessidade. Você pode considerar `ngx.timer` como uma solicitação de cliente simulada pelo OpenResty para acionar a função de callback correspondente.

As tarefas de timer do OpenResty podem ser divididas nos dois tipos a seguir.

- `ngx.timer.at` é usado para executar tarefas de timer únicas.
- `ngx.timer.every` é usado para executar tarefas de timer de período fixo.

Lembra da pergunta instigante que deixei no final do último artigo? A pergunta era como quebrar a restrição de que `cosocket` não pode ser usado em `init_worker_by_lua`, e a resposta é `ngx.timer`.

O código a seguir inicia uma tarefa de timer com um atraso de `0`. Ele inicia a função de callback `handler`, e nessa função, ele usa `cosocket` para acessar um site.

```
init_worker_by_lua_block {
    local function handler()
        local sock = ngx.socket.tcp()
        local ok, err = sock:connect(“api7.ai", 80)
    end

    local ok, err = ngx.timer.at(0, handler)
}
```

Dessa forma, contornamos a restrição de que `cosocket` não pode ser usado nessa fase.

Voltando à necessidade do usuário que mencionamos no início desta seção, `ngx.timer.at` não atende à necessidade de execução periódica; no exemplo de código acima, é uma tarefa única.

Então, como fazemos isso periodicamente? Parece que você tem duas opções com base na API `ngx.timer.at`.

- Você pode implementar a tarefa periódica manualmente usando um loop infinito `while true` na função de callback que `sleep` por um tempo após executar a tarefa.
- Você também pode criar um novo timer no final da função de callback.

No entanto, antes de fazer uma escolha, há uma coisa que precisamos esclarecer: o timer é essencialmente uma solicitação, embora a solicitação não tenha sido iniciada pelo cliente. Para a solicitação, ela deve sair após concluir sua tarefa e não pode ficar residente para sempre. Caso contrário, é fácil causar vários tipos de vazamento de recursos.

Portanto, a primeira solução de usar `while true` para implementar tarefas periódicas não é confiável. A segunda solução é viável, mas cria timers recursivamente, o que não é fácil de entender.

Então, existe uma solução melhor? A nova API `ngx.timer.every` do OpenResty foi projetada especificamente para resolver esse problema e é uma solução mais próxima do `crontab`.

A desvantagem é que você nunca tem a chance de cancelar uma tarefa de timer após iniciá-la. Afinal, `ngx.timer.cancel` ainda é uma função a ser implementada.

Nesse ponto, você enfrentará um problema: o timer está sendo executado em segundo plano e não pode ser cancelado; se houver muitos timers, é fácil esgotar os recursos do sistema.

Portanto, o OpenResty fornece duas diretivas, `lua_max_pending_timers` e `lua_max_running_timers` para limitá-los. A primeira representa o número máximo de timers aguardando execução, e a segunda representa o número máximo de timers em execução atualmente.

Você também pode usar a API Lua para obter os valores das tarefas de timer atualmente em espera e em execução, como mostrado nos dois exemplos a seguir.

```
content_by_lua_block {
    ngx.timer.at(3, function() end)
    ngx.say(ngx.timer.pending_count())
}
```

Este código imprimirá um `1`, indicando que há uma tarefa agendada aguardando execução.

```
content_by_lua_block {
    ngx.timer.at(0.1, function() ngx.sleep(0.3) end)
    ngx.sleep(0.2)
    ngx.say(ngx.timer.running_count())
}
```

Este código imprimirá um `1`, indicando que há uma tarefa agendada em execução.

## Processo Privilegiado

A seguir, vamos ver o processo privilegiado. Como todos sabemos, o NGINX é dividido em processo `Master` e processos `Worker`, onde os processos worker lidam com as solicitações dos usuários. Podemos obter o tipo do processo através da API `process.type` fornecida em `lua-resty-core`. Por exemplo, você pode usar `resty` para executar a seguinte função.

```
$ resty -e 'local process = require "ngx.process"
ngx.say("process type:", process.type())'
```

Você verá que ele retorna um resultado `single` em vez de `worker`, o que significa que o `resty` inicia o NGINX com um processo `Worker`, não um processo `Master`. Isso é verdade. Na implementação do `resty`, você pode ver que o processo `Master` é desativado com uma linha como esta.

```
master_process off;
```

O OpenResty estende o NGINX adicionando um `privileged agent`. O processo privilegiado tem as seguintes características especiais.

- Ele não monitora nenhuma porta, o que significa que não fornece serviços para o exterior.
- Ele tem os mesmos privilégios que o processo `Master`, que geralmente são os privilégios do usuário `root`, permitindo que ele faça muitas tarefas que são impossíveis para o processo `Worker`.
- O processo privilegiado só pode ser aberto no contexto `init_by_lua`.
- Além disso, o processo privilegiado só faz sentido se for executado no contexto `init_worker_by_lua`, porque nenhuma solicitação é acionada, e eles não vão para os contextos `content`, `access`, etc.

Vejamos um exemplo de um processo privilegiado que é ativado.

```
init_by_lua_block {
    local process = require "ngx.process"

    local ok, err = process.enable_privileged_agent()
    if not ok then
        ngx.log(ngx.ERR, "enables privileged agent failed error:", err)
    end
}
```

Após abrir o processo privilegiado com este código e iniciar o serviço OpenResty, podemos ver que o processo privilegiado agora faz parte do processo NGINX.

```
nginx: master process
nginx: worker process
nginx: privileged agent process
```

No entanto, se os privilégios forem executados apenas uma vez durante a fase `init_worker_by_lua`, o que não é uma boa ideia, como devemos acionar o processo privilegiado?

Sim, a resposta está escondida no conhecimento que acabamos de ensinar. Como ele não escuta portas, ou seja, não pode ser acionado por solicitações de terminal, a única maneira de acioná-lo periodicamente é usar o `ngx.timer` que acabamos de introduzir:

```
init_worker_by_lua_block {
    local process = require "ngx.process"

    local function reload(premature)
        local f, err = io.open(ngx.config.prefix() .. "/logs/nginx.pid", "r")
        if not f then
            return
        end
        local pid = f:read()
        f:close()
        os.execute("kill -HUP " .. pid)
    end

    if process.type() == "privileged agent" then
         local ok, err = ngx.timer.every(5, reload)
        if not ok then
            ngx.log(ngx.ERR, err)
        end
    end
}
```

O código acima implementa a capacidade de enviar sinais `HUP` para o processo master a cada 5 segundos. Naturalmente, você pode construir sobre isso para fazer coisas mais emocionantes, como consultar o banco de dados para ver se há tarefas para o processo privilegiado e executá-las. Como o processo privilegiado tem privilégios de `root`, isso obviamente é um pouco de um programa "backdoor".

## `ngx.pipe` Não Bloqueante

Finalmente, vejamos o `ngx.pipe` não bloqueante, que usa a biblioteca padrão do Lua para executar um comando externo que envia um sinal para o processo `Master` no exemplo de código que acabamos de descrever.

```
os.execute("kill -HUP " .. pid)
```

Naturalmente, essa operação será bloqueante. Então, existe uma maneira não bloqueante de chamar programas externos no OpenResty? Afinal, você sabe que se estiver usando o OpenResty como uma plataforma de desenvolvimento completa e não como um servidor web, isso é o que você precisa. Por esse motivo, a biblioteca `lua-resty-shell` foi criada, e usá-la para invocar a linha de comando é não bloqueante:

```
$ resty -e 'local shell = require "resty.shell"
local ok, stdout, stderr, reason, status =
    shell.run([[echo "hello, world"]])
    ngx.say(stdout)
```

Este código é uma maneira diferente de escrever `hello world`, chamando o comando `echo` do sistema para completar a saída. Da mesma forma, você pode usar `resty.shell` como uma alternativa à chamada `os.execute` em Lua.

Sabemos que a implementação subjacente do `lua-resty-shell` depende da API `ngx.pipe` em `lua-resty-core`, então este exemplo usa `lua-resty-shell` para imprimir `hello world`, usando `ngx.pipe` em vez disso, ficaria assim.

```
$ resty -e 'local ngx_pipe = require "ngx.pipe"
local proc = ngx_pipe.spawn({"echo", "hello world"})
local data, err = proc:stdout_read_line()
ngx.say(data)'
```

O acima é o código subjacente da implementação do `lua-resty-shell`. Você pode verificar a documentação e os casos de teste do `ngx.pipe` para obter mais informações sobre como usá-lo. Portanto, não vou me aprofundar aqui.

## Resumo

É isso. Terminamos com o conteúdo principal de hoje. A partir dos recursos acima, podemos ver que o OpenResty também está tentando se aproximar da direção de uma plataforma universal enquanto faz um NGINX melhor, esperando que os desenvolvedores possam tentar unificar a pilha de tecnologia e usar o OpenResty para resolver suas necessidades de desenvolvimento. Isso é bastante amigável para operações e manutenção, pois os custos de manutenção são menores, desde que você implante um OpenResty nele.

Finalmente, vou deixar uma pergunta instigante. Como pode haver vários `Worker`s do NGINX, o `timer` será executado uma vez para cada `Worker`, o que é inaceitável na maioria dos cenários. Como podemos garantir que o `timer` seja executado apenas uma vez?

Sinta-se à vontade para deixar um comentário com sua solução e compartilhe este artigo com seus colegas e amigos para que possamos nos comunicar e melhorar 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

- [Tarefas de Timer](#tarefas-de-timer)
- [Processo Privilegiado](#processo-privilegiado)
- [`ngx.pipe` Não Bloqueante](#ngxpipe-n%C3%A3o-bloqueante)
- [Resumo](#resumo)
