---
title: "Jenseits des Web-Servers: Privilegierte Prozesse und Timer-Tasks - API7.ai"
description: "Privileged Process und Timer Tasks sind wichtige Funktionen, die OpenResty von NGINX und anderen Webservern unterscheiden."
url: "https://api7.ai/de/learning-center/openresty/privileged-process-and-timer-tasks"
---

# Jenseits des Web-Servers: Privilegierte Prozesse und Timer-Tasks

API7.ai

November 3, 2022

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

Im vorherigen Artikel haben wir die OpenResty-APIs, `shared dict` und `cosocket` vorgestellt, die alle Funktionen im Bereich von NGINX und Webservern implementieren und einen programmierbaren Webserver bieten, der kostengünstiger und einfacher zu warten ist.

Allerdings kann OpenResty noch mehr. Lassen Sie uns heute einige Funktionen in OpenResty vorstellen, die über den Webserver hinausgehen. Es handelt sich um Timer-Aufgaben, privilegierte Prozesse und nicht-blockierendes `ngx.pipe`.

## Timer-Aufgaben

In OpenResty müssen wir manchmal regelmäßig bestimmte Aufgaben im Hintergrund ausführen, wie z.B. das Synchronisieren von Daten oder das Bereinigen von Protokollen. Wie würden Sie das gestalten? Der einfachste Ansatz wäre, eine API-Schnittstelle bereitzustellen, die diese Aufgaben ausführt, und dann das System-`crontab` zu verwenden, um in regelmäßigen Abständen `curl` aufzurufen und diese Schnittstelle zu nutzen, um die Anforderung indirekt zu erfüllen.

Dies wäre jedoch nicht nur fragmentiert, sondern würde auch die Komplexität der Wartung erhöhen. Daher bietet OpenResty `ngx.timer`, um diese Art von Anforderung zu lösen. Sie können sich `ngx.timer` als einen von OpenResty simulierten Client-Request vorstellen, der die entsprechende Callback-Funktion auslöst.

Die Timer-Aufgaben in OpenResty können in die folgenden zwei Typen unterteilt werden:

- `ngx.timer.at` wird verwendet, um einmalige Timer-Aufgaben auszuführen.
- `ngx.timer.every` wird verwendet, um periodische Timer-Aufgaben auszuführen.

Erinnern Sie sich an die nachdenkliche Frage, die ich am Ende des letzten Artikels gestellt habe? Die Frage war, wie man die Einschränkung umgehen kann, dass `cosocket` nicht in `init_worker_by_lua` verwendet werden kann, und die Antwort lautet `ngx.timer`.

Der folgende Code startet eine Timer-Aufgabe mit einer Verzögerung von `0`. Er startet die Callback-Funktion `handler`, und in dieser Funktion wird `cosocket` verwendet, um auf eine Website zuzugreifen.

```
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)
}
```

Auf diese Weise umgehen wir die Einschränkung, dass `cosocket` in dieser Phase nicht verwendet werden kann.

Zurück zu der Benutzeranforderung, die wir zu Beginn dieses Abschnitts erwähnt haben: `ngx.timer.at` erfüllt nicht die Anforderung, periodisch ausgeführt zu werden; im obigen Codebeispiel handelt es sich um eine einmalige Aufgabe.

Wie können wir dies also periodisch tun? Sie scheinen zwei Optionen basierend auf der `ngx.timer.at`-API zu haben:

- Sie können die periodische Aufgabe selbst implementieren, indem Sie eine `while true`-Endlosschleife in der Callback-Funktion verwenden, die nach der Ausführung der Aufgabe eine Weile `sleep`t.
- Sie können auch am Ende der Callback-Funktion einen neuen Timer erstellen.

Bevor Sie jedoch eine Wahl treffen, müssen wir eines klären: Der Timer ist im Wesentlichen ein Request, obwohl dieser Request nicht vom Client initiiert wurde. Für den Request muss er nach Abschluss seiner Aufgabe beendet werden und kann nicht dauerhaft aktiv bleiben. Andernfalls kann dies leicht zu verschiedenen Arten von Ressourcenlecks führen.

Daher ist die erste Lösung, periodische Aufgaben mit `while true` zu implementieren, unzuverlässig. Die zweite Lösung ist machbar, erstellt jedoch rekursiv Timer, was nicht leicht zu verstehen ist.

Gibt es also eine bessere Lösung? Die neue `ngx.timer.every`-API in OpenResty wurde speziell entwickelt, um dieses Problem zu lösen, und ist eine Lösung, die näher an `crontab` liegt.

Der Nachteil ist, dass Sie eine Timer-Aufgabe nach dem Start nie mehr abbrechen können. Schließlich ist `ngx.timer.cancel` immer noch eine To-Do-Funktion.

An diesem Punkt stehen Sie vor einem Problem: Der Timer läuft im Hintergrund und kann nicht abgebrochen werden; wenn es viele Timer gibt, können die Systemressourcen leicht erschöpft sein.

Daher bietet OpenResty zwei Direktiven, `lua_max_pending_timers` und `lua_max_running_timers`, um sie zu begrenzen. Ersteres stellt die maximale Anzahl von Timern dar, die auf die Ausführung warten, und Letzteres die maximale Anzahl der derzeit laufenden Timer.

Sie können auch die Lua-API verwenden, um die Werte der derzeit wartenden und laufenden Timer-Aufgaben zu erhalten, wie in den folgenden beiden Beispielen gezeigt.

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

Dieser Code gibt eine `1` aus, was bedeutet, dass eine geplante Aufgabe auf die Ausführung wartet.

```
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())
}
```

Dieser Code gibt eine `1` aus, was bedeutet, dass eine geplante Aufgabe ausgeführt wird.

## Privilegierter Prozess

Als Nächstes betrachten wir den privilegierten Prozess. Wie wir alle wissen, ist NGINX in `Master`-Prozess und `Worker`-Prozesse unterteilt, wobei die Worker-Prozesse Benutzeranfragen bearbeiten. Wir können den Typ des Prozesses über die in `lua-resty-core` bereitgestellte `process.type`-API erhalten. Zum Beispiel können Sie `resty` verwenden, um die folgende Funktion auszuführen.

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

Sie werden sehen, dass es ein `single`-Ergebnis zurückgibt und nicht `worker`, was bedeutet, dass `resty` NGINX mit einem `Worker`-Prozess startet, nicht mit einem `Master`-Prozess. Dies ist wahr. In der `resty`-Implementierung können Sie sehen, dass der `Master`-Prozess mit einer Zeile wie dieser deaktiviert wird.

```
master_process off;
```

OpenResty erweitert NGINX durch das Hinzufügen eines `privileged agent`. Der privilegierte Prozess hat die folgenden besonderen Merkmale:

- Er überwacht keine Ports, was bedeutet, dass er keine Dienste nach außen bereitstellt.
- Er hat die gleichen Privilegien wie der `Master`-Prozess, was in der Regel die Privilegien des `root`-Benutzers sind, was es ihm ermöglicht, viele Aufgaben auszuführen, die für den `Worker`-Prozess unmöglich sind.
- Der privilegierte Prozess kann nur im `init_by_lua`-Kontext geöffnet werden.
- Außerdem macht der privilegierte Prozess nur Sinn, wenn er im `init_worker_by_lua`-Kontext läuft, da keine Anfragen ausgelöst werden und sie nicht in die `content`-, `access`- usw. Kontexte gelangen.

Schauen wir uns ein Beispiel für einen privilegierten Prozess an, der geöffnet wird.

```
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
}
```

Nachdem der privilegierte Prozess mit diesem Code geöffnet und der OpenResty-Dienst gestartet wurde, können wir sehen, dass der privilegierte Prozess jetzt Teil des NGINX-Prozesses ist.

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

Wenn jedoch Privilegien nur einmal während der `init_worker_by_lua`-Phase ausgeführt werden, was keine gute Idee ist, wie sollten wir dann den privilegierten Prozess auslösen?

Ja, die Antwort liegt in dem gerade vermittelten Wissen. Da er keine Ports überwacht, d.h. er kann nicht durch Terminalanfragen ausgelöst werden, ist die einzige Möglichkeit, ihn periodisch auszulösen, die Verwendung des gerade vorgestellten `ngx.timer`:

```
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
}
```

Der obige Code implementiert die Fähigkeit, alle 5 Sekunden `HUP`-Signale an den Master-Prozess zu senden. Natürlich können Sie darauf aufbauen, um noch spannendere Dinge zu tun, wie z.B. die Datenbank abzufragen, um zu sehen, ob es Aufgaben für den privilegierten Prozess gibt, und diese auszuführen. Da der privilegierte Prozess `root`-Privilegien hat, ist dies offensichtlich ein bisschen wie ein "Hintertür"-Programm.

## Nicht-blockierendes `ngx.pipe`

Schließlich betrachten wir das nicht-blockierende `ngx.pipe`, das die Standardbibliothek von Lua verwendet, um einen externen Befehl auszuführen, der in dem gerade beschriebenen Codebeispiel ein Signal an den `Master`-Prozess sendet.

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

Natürlich blockiert dieser Vorgang. Gibt es also eine nicht-blockierende Möglichkeit, externe Programme in OpenResty aufzurufen? Schließlich wissen Sie, dass Sie, wenn Sie OpenResty als vollständige Entwicklungsplattform und nicht als Webserver verwenden, dies benötigen. Aus diesem Grund wurde die `lua-resty-shell`-Bibliothek erstellt, und ihre Verwendung zum Aufrufen der Befehlszeile ist nicht-blockierend:

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

Dieser Code ist eine andere Art, `hello world` zu schreiben, indem der Systembefehl `echo` verwendet wird, um die Ausgabe zu erledigen. Ebenso können Sie `resty.shell` als Alternative zum `os.execute`-Aufruf in Lua verwenden.

Wir wissen, dass die zugrunde liegende Implementierung von `lua-resty-shell` auf der `ngx.pipe`-API in `lua-resty-core` basiert, daher würde dieses Beispiel, das `lua-resty-shell` verwendet, um `hello world` auszugeben, mit `ngx.pipe` so aussehen.

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

Das obige ist der zugrunde liegende Code der `lua-resty-shell`-Implementierung. Sie können die `ngx.pipe`-Dokumentation und Testfälle überprüfen, um mehr über die Verwendung zu erfahren. Daher werde ich hier nicht weiter darauf eingehen.

## Zusammenfassung

Das war's. Wir haben den Hauptinhalt für heute abgeschlossen. Aus den oben genannten Funktionen können wir sehen, dass OpenResty auch versucht, sich in Richtung einer universellen Plattform zu bewegen, während es ein besserer NGINX wird, in der Hoffnung, dass Entwickler versuchen können, den Technologie-Stack zu vereinheitlichen und OpenResty zu verwenden, um ihre Entwicklungsanforderungen zu lösen. Dies ist ziemlich freundlich für den Betrieb und die Wartung, da die Wartungskosten niedriger sind, solange Sie ein OpenResty darauf bereitstellen.

Abschließend hinterlasse ich Ihnen eine nachdenkliche Frage. Da es möglicherweise mehrere NGINX-`Worker` gibt, wird der `Timer` einmal für jeden `Worker` ausgeführt, was in den meisten Szenarien nicht akzeptabel ist. Wie können wir sicherstellen, dass der `Timer` nur einmal ausgeführt wird?

Hinterlassen Sie gerne einen Kommentar mit Ihrer Lösung und teilen Sie diesen Artikel gerne mit Ihren Kollegen und Freunden, damit wir gemeinsam kommunizieren und uns verbessern können.

Share article link

Share

Related Posts

[![OpenResty FAQ | Wie OpenResty in der Praxis eingesetzt wird](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)

## [OpenResty FAQ | Wie OpenResty in der Praxis eingesetzt wird](https://api7.ai/learning-center/openresty/faq-5)

[![Teil 3: Wie man ein Microservices API Gateway mit OpenResty erstellt](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)

## [Teil 3: Wie man ein Microservices API Gateway mit OpenResty erstellt](https://api7.ai/learning-center/openresty/how-to-build-a-microservices-api-gateway-part-3)

Table of Contents

- [Timer-Aufgaben](#timer-aufgaben)
- [Privilegierter Prozess](#privilegierter-prozess)
- [Nicht-blockierendes `ngx.pipe`](#nicht-blockierendes-ngxpipe)
- [Zusammenfassung](#zusammenfassung)
