OpenResty desde el inicio hasta la competencia 20 más allá del servidor web: proceso privilegiado y tareas programadas

20 | Más allá del servidor web: procesos privilegiados y tareas programadas

Hola, soy Wen Ming.

Anteriormente introdujimos la API de OpenResty, el caché de diccionario compartido y el cosocket. Las funciones que implementan están todas dentro del alcance de Nginx y los servidores web, lo que puede considerarse como una implementación con costos de desarrollo más bajos y un mantenimiento más sencillo, y proporciona un servidor web programable.

Sin embargo, OpenResty no se detiene allí. Elijamos algunos hoy e introduzcamos las funciones más allá del servidor web en OpenResty. Son tareas programadas, procesos privilegiados y ngx.pipe sin bloqueo, respectivamente.

tarea cronometrada

En OpenResty, a veces necesitamos realizar periódicamente ciertas tareas en segundo plano, como sincronizar datos, limpiar registros, etc. Si te pidieran diseñar, ¿qué harías? La forma más fácil de pensar es proporcionar una interfaz API al mundo exterior y completar estas tareas en la interfaz; luego usar el crontab del sistema para llamar a curl regularmente para acceder a esta interfaz y luego realizar este requisito en una curva.

Sin embargo, de esta manera, no solo habrá una sensación de fragmentación, sino que también traerá una mayor complejidad a la operación y el mantenimiento. Por lo tanto, OpenResty proporciona ngx.timerpara resolver este tipo de necesidades. Puede ngx.timerconsiderarlo como una solicitud de cliente simulada por OpenResty para activar la función de devolución de llamada correspondiente.

De hecho, las tareas programadas de OpenResty se pueden dividir en los siguientes dos tipos:

  • ngx.timer.at, utilizado para ejecutar una tarea de temporización única;
  • ngx.time.every, utilizado para ejecutar tareas de temporización de período fijo.

¿Recuerdas las preguntas de reflexión que dejé al final de la última clase? La pregunta es cómo superar la limitación init_worker_by_luaen , y esta respuesta es en realidad ngx.timer.

El siguiente código inicia una tarea programada con un retraso de 0. Inicia la función de devolución de llamada handlery, en esta función, usa cosocket para visitar un sitio web:

init_worker_by_lua_block {
        local function handler()
            local sock = ngx.socket.tcp()
            local ok, err = sock:connect(“www.baidu.com", 80)
        end

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

De esta manera, evitamos la limitación de que los cosockets no se pueden usar en esta etapa.

Volviendo a las necesidades del usuario que mencionamos al principio de esta parte, ngx.timer.atno resuelve la necesidad de ejecutar periódicamente.En el ejemplo de código anterior, es una tarea de una sola vez.

Entonces, ¿cómo lograr un funcionamiento periódico? En la superficie, según ngx.timer.atesta API, tiene dos opciones:

  • Puede usar un bucle infinito verdadero en la función de devolución de llamada, dormir por un tiempo después de ejecutar la tarea e implementar la tarea periódica usted mismo;
  • También puede crear otro temporizador nuevo al final de la función de devolución de llamada.

Sin embargo, antes de hacer una elección, debemos aclarar una cosa: la esencia de un temporizador es una solicitud, aunque esta solicitud no la inicia el terminal; en cuanto a la solicitud, saldrá después de completar su propia tarea, y no puede Siempre permanente, de lo contrario es fácil causar fugas de varios recursos.

Por lo tanto, el primer esquema de usar while true para implementar tareas periódicas por sí solo no es confiable. Aunque la segunda solución es factible, la creación recursiva de temporizadores no es fácil de entender para las personas.

Entonces, ¿hay una solución mejor? De hecho, la ngx.time.everyAPI recién agregada detrás de OpenResty está diseñada para resolver este problema y es una solución más cercana a crontab.

Pero la mosca en el ungüento es que después de iniciar un temporizador, no tiene posibilidad de cancelar la tarea programada, después de todo, ngx.timer.cancelsigue siendo una función de tareas pendientes.

En este momento, se enfrentará a un problema: las tareas programadas se ejecutan en segundo plano y no se pueden cancelar; si hay una gran cantidad de tareas programadas, es fácil agotar los recursos del sistema.

Por lo tanto, OpenResty lua_max_pending_timersproporciona lua_max_running_timersestas dos instrucciones para limitarlo. El primero representa el valor máximo de las tareas programadas que esperan ser ejecutadas, y el segundo representa el valor máximo de las tareas programadas actualmente en ejecución.

También puede usar la API de Lua para obtener el valor de la tarea programada que actualmente espera para ser ejecutada y se está ejecutando.Los siguientes son dos ejemplos:

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

Este código imprimirá 1, lo que indica que hay 1 tarea programada esperando a ser ejecutada.

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á 1, lo que indica que hay 1 tarea programada en ejecución.

proceso privilegiado

Luego mira el proceso privilegiado. Todos sabemos que Nginx se divide principalmente en proceso maestro y proceso de trabajo, entre los cuales, el proceso de trabajo realmente maneja las solicitudes de los usuarios. lua-resty-corePodemos process.typeobtener el tipo de proceso a través de la API proporcionada en . Por ejemplo, puede restyejecutar la siguiente función con:

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

Verás que no devuelve un resultado worker, sino single. Esto significa que restyel Nginx iniciado solo tiene procesos de trabajo y ningún proceso maestro. De hecho, lo mismo es cierto. En la implementación restyde , puede ver que la siguiente línea de configuración cierra el proceso maestro:

master_process off;

OpenResty se ha expandido sobre la base de Nginx y agregó un proceso privilegiado: agente privilegiado. Los procesos privilegiados son especiales:

  • No escucha ningún puerto, lo que significa que no proporcionará ningún servicio externo;
  • Tiene los mismos permisos que el proceso maestro, en términos generales, son rootlos permisos del usuario , lo que le permite realizar muchas tareas que son imposibles para el proceso de trabajo;
  • Los procesos privilegiados solo pueden iniciarse en init_by_luael contexto ;
  • Además, el proceso privilegiado solo tiene sentido si se ejecuta en init_worker_by_luael contexto , porque no hay una solicitud para desencadenar, y no irá al contexto de content, accessetc.

A continuación, veamos un ejemplo de cómo iniciar un proceso privilegiado:

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
}

Después de iniciar el proceso privilegiado a través de este código y luego iniciar el servicio OpenResty, podemos ver que hay más procesos privilegiados en el proceso Nginx:

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

Sin embargo, si el privilegio solo se ejecuta una vez en init_worker_by_luala etapa , lo que obviamente no es una buena idea, ¿cómo deberíamos activar el proceso privilegiado?

Así es, la respuesta está en el conocimiento que acabamos de mencionar. Dado que no escucha puertos, es decir, no puede activarse mediante solicitudes de terminal, solo puede activarse ngx.timerperiódicamente utilizando lo que acabamos de presentar:

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
}

El código anterior implementa la función de enviar el semáforo HUP al proceso maestro cada 5 segundos. Naturalmente, también puedes implementar funciones más interesantes sobre esta base, como sondear la base de datos para ver si hay tareas de procesos privilegiados y ejecutarlas. Debido a que el proceso privilegiado tiene autoridad de raíz, esto es obviamente un poco como un programa de "puerta trasera".

ngx.pipe sin bloqueo

Finalmente, observamos el ngx.pipe que no bloquea. En el ejemplo de código que acabamos de mencionar, usamos la biblioteca estándar de Lua para ejecutar la línea de comando externa y enviar la señal al proceso maestro:

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

Esta operación está naturalmente bloqueada. Entonces, en OpenResty, ¿hay una forma sin bloqueo de llamar a programas externos? Después de todo, sepa que si está utilizando OpenResty como una plataforma de desarrollo completa en lugar de un servidor web, esto es exactamente lo que necesita.

Con este fin, lua-resty-shellse creó la biblioteca, y usarla para llamar a la línea de comando no bloquea:

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

Este código puede considerarse como otra forma de escribir hola mundo, llama al echocomando para completar la salida. Del mismo modo, puede usar resty.shellen lugar de os.executellamar .

Sabemos que lua-resty-shellla implementación subyacente de , se basa lua-resty-coreen la API [ ngx.pipe ] en , por lo que lua-resty-shelleste hello wroldejemplo de uso para imprimir ngx.pipese puede escribir de la siguiente manera:

$ 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)'

Este es en realidad lua-resty-shellel código de implementación subyacente . Puede ngx.pipeconsultar la documentación y los casos de prueba para obtener más métodos de uso, por lo que no entraré en detalles aquí.

escribir al final

En este punto, he terminado el contenido principal de hoy. De las funciones anteriores, podemos ver que OpenResty también está tratando de acercarse a la plataforma general con la premisa de hacer un mejor Nginx. Esperamos que los desarrolladores puedan intentar unificar la pila de tecnología y usar OpenResty para resolver las necesidades de desarrollo. Esto es muy amigable para la operación y el mantenimiento, porque solo se implementa un OpenResty y el costo de mantenimiento es menor.

Finalmente, los dejo con una pregunta de reflexión. Dado que puede haber varios trabajadores de Nginx, el temporizador se ejecutará una vez en cada trabajador, lo que es inaceptable en la mayoría de los escenarios. ¿Cómo debemos asegurarnos de que el temporizador se ejecuta solo una vez?

Bienvenido a dejar un mensaje para hablar sobre su solución, y puede compartir este artículo con sus colegas y amigos. Podemos comunicarnos y progresar juntos.

Supongo que te gusta

Origin blog.csdn.net/fegus/article/details/130740378
Recomendado
Clasificación