txid: Generiere sortierbare, eindeutige Transaktions- oder Anfrage-IDs für nginx-module-lua/nginx
Installation
Wenn du das RPM-Repository-Abonnement noch nicht eingerichtet hast, melde dich an. Dann kannst du mit den folgenden Schritten fortfahren.
CentOS/RHEL 7 oder Amazon Linux 2
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-txid
CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-txid
Um diese Lua-Bibliothek mit NGINX zu verwenden, stelle sicher, dass nginx-module-lua installiert ist.
Dieses Dokument beschreibt lua-resty-txid v1.0.0, das am 01. April 2018 veröffentlicht wurde.
lua-resty-txid bietet eine Funktion, die verwendet werden kann, um eindeutige Transaktions-/Anfrage-IDs für OpenResty/nginx zu generieren. Die IDs können verwendet werden, um Protokolle oder Upstream-Anfragen zu korrelieren und haben folgende Eigenschaften:
- 20 Zeichen
- base32hex kodiert
- Zeitlich und lexikalisch sortierbar
- Groß-/Kleinschreibung ignorierend
- 96-Bit-Identifikator
lua-resty-txid ist ein LuaJIT-Port von ngx_txid für OpenResty (oder nginx mit ngx_lua). Die von lua-resty-txid generierten IDs folgen dem genau gleichen Muster und sind mit ngx_txid kompatibel.
Verwendung
Eine einzelne txid() Lua-Funktion wird von diesem Modul bereitgestellt, um IDs zu generieren:
local txid = require "resty.txid"
local id = txid() -- b2g6q94qdn6h84an7vfg
Jedes Mal, wenn txid() aufgerufen wird, wird eine neue, eindeutige ID zurückgegeben, daher musst du das Ergebnis zwischenspeichern, wenn du dieselbe ID an mehreren Stellen für eine einzelne Anfrage wiederverwenden möchtest. Abhängig von deiner Verwendung bieten ngx.ctx oder set_by_lua einige einfache Optionen, um den Wert pro Anfrage zwischenzuspeichern.
txid() -- b2g83t2oshrg092mjggg
txid() -- b2g83t2oodncokuges00
ngx.ctx.txid = txid() -- b2g83t2od939mdvb2l0g
ngx.ctx.txid -- b2g83t2od939mdvb2l0g
Schließlich akzeptiert txid() ein optionales Argument für den Zeitstempel (in Millisekunden), der bei der Generierung der ID verwendet werden soll. Standardmäßig wird der aktuelle Zeitstempel verwendet. Da die resultierenden IDs zeitlich und lexikalisch sortierbar sind, kann dies verwendet werden, um IDs zu generieren, die basierend auf einem vorherigen Datum oder einer vorherigen Uhrzeit sortiert werden.
local timestamp_ms = 655829050000 -- 1990-10-13 14:44:10
txid(timestamp_ms) -- 4om9qi54la8ffr4bd9sg
local timestamp_ms = 655929050000 -- 1990-10-14 12:30:50
txid(timestamp_ms) -- 4on1lg74nt0ud2ssllu0
Beispiel
Ein vollständigeres Beispiel mit Caching, Setzen von Anfrage-/Antwort-Headern und Integration mit dem Logging von nginx:
http {
log_format agent "$lua_txid $http_user_agent";
log_format addr "$lua_txid $remote_addr";
init_by_lua_block {
# Modul vorladen.
require "resty.txid"
}
server {
listen 8080;
access_log logs/agents.log agent;
access_log logs/addrs.log addr;
# Setze eine nginx-Variable, die pro Anfrage zwischengespeichert wird und in der
# nginx log_format verwendet werden kann.
set_by_lua_block $lua_txid {
local txid = require "resty.txid"
return txid()
}
location / {
# Setze einen Header in der Antwort, der die ID bereitstellt.
more_set_headers "X-Request-Id: $lua_txid";
# Setze einen Header in der Anfrage, der die ID bereitstellt (die an den
# proxied upstream gesendet wird).
more_set_input_headers "X-Request-Id: $lua_txid";
proxy_pass http://localhost:8081;
}
}
}
Leistung
Benchmarks zeigen, dass die Leistung der ngx_txid C-Erweiterung entspricht.
Design
Das Design der Transaktions-ID ist ein direkter Port von ngx_txid, daher hier alle ursprünglichen Informationen über das Design von ngx_txid:
Hintergrund
Das Design dieser Transaktions-ID sollte die folgenden Anforderungen erfüllen:
- Zeitlich ungefähr numerisch sortierbar mit ~Sekunden-Granularität.
- Eine Darstellung haben, die ungefähr lexikalisch sortierbar ist mit ~Sekunden-Granularität.
- Eine Wahrscheinlichkeit von weniger als 1e-9 für Kollisionen bei 1 Million Transaktionen pro Sekunde haben.
- Effizient und einfach in feste C-Typen dekodierbar sein.
- Immer verfügbar sein, auch auf das Risiko einer höheren Kollisionswahrscheinlichkeit.
- So wenige Bytes wie möglich verwenden.
- Mit IPv4- und IPv6-Netzwerken arbeiten.
Technik
Verwende eine monotone Millisekundenauflösung in den oberen 42 Bits und Systementropie für die unteren 54 Bits. Verwende genügend Entropie-Bits, um eine Kollisionswahrscheinlichkeit bei einer gewünschten globalen Anfrage-Rate zu erfüllen.
+------------- 64 bits------------+--- 32 bits ----+
+------ 42 bits ------+--22 bits--|----------------+
| msec since 1970-1-1 | random | random |
+---------------------+-----------+----------------+
Eine Anfrage-Rate von 1 Million pro Sekunde über alle Server bedeutet 1000 zufällige Werte pro Millisekunde. Die Schätzung der Kollisionswahrscheinlichkeit unter Verwendung des Geburtstagsparadoxons kann mit dieser Formel erfolgen: 1 - e^(-((m^2)/(2*n))), wobei m die Anzahl der IDs und n die Anzahl der möglichen Zufallswerte ist.
Bei Verwendung von 54 Bits Entropie:
1mil req/s = 1 - exp(-((1000^2) /(2*2^54))) = 2.775558e-11
10mil req/s = 1 - exp(-((10000^2)/(2*2^54))) = 2.775558e-09
Die Wahrscheinlichkeit einer Kollision ist selbst bei 10 Millionen Anfragen pro Sekunde gering.
Nginx verfolgt die aktuelle Uhr in Schritten der Konfigurationsdirektive timer_resolution. Die Uhrauflösung für $txid beträgt 1 ms, sodass eine Timerauflösung von mehr als 1 ms bedeutet, dass die Wahrscheinlichkeit einer Kollision steigt. Wenn du eine timer_resolution von 10 ms hast, würde eine Million Anfragen pro Sekunde im schlimmsten Fall 10.000 zufällige Werte pro Sekunde erfordern.
Kodierung
base32hex wird mit einem Kleinbuchstabenalphabet und ohne Padding-Zeichen aus den folgenden Gründen verwendet:
- Lexikalische Sortierreihenfolge entspricht der numerischen Sortierreihenfolge.
- Groß-/Kleinschreibung ignorierende Gleichheit.
- Kleinbuchstaben sind leichter für visuelle Vergleiche.
- Dichter als hexadezimale Kodierung um 4 Bytes.
Weitere Techniken
- snowflake: Verwendet Zeit(41) + eindeutige ID(10) + Sequenz(12).
- Pro: Garantierte eindeutige Sequenzen.
- Pro: Passt in 63 Bits.
- Contra: Erfordert eindeutige ID-Koordination für jeden Server - 16 Worker-Prozesse pro Host bedeuten eine Grenze von 64 Instanzen von nginx.
- Contra: Nur 11 Bits für eindeutige IDs verfügbar, benötigt Überwachung.
- Contra: Totale Ordnung nur im gleichen Prozess möglich.
-
Contra: Dienstunterbrechung möglich, wenn Uhren die Synchronisation verlieren.
-
flake: Verwendet Zeit + MAC-ID + Sequenz.
- Pro: Garantierte eindeutige Sequenzen.
- Contra: Verwendet 128 Bits.
- Contra: Verschwendet 22 Bits Zeitstempeldaten.
- Contra: Nur ein einzelner Prozess pro Host kann IDs generieren - muss den Zugriff auf die Sequenz von jedem Worker-Prozess synchronisieren.
- Contra: Dienstunterbrechung möglich, wenn Uhren die Synchronisation verlieren.
-
Contra: Seeds plattformübergreifende MAC-Adressensuche.
-
UUIDv4: 122 Bits Entropie.
- Pro: Sehr geringe Wahrscheinlichkeit einer Kollision.
-
Contra: Nicht sortierbar.
-
UUID mit Zeitstempel: 48 Bits Zeit + 74 Bits Entropie.
- Pro: Sehr geringe Wahrscheinlichkeit einer Kollision.
-
Contra: Stringdarstellung ist nicht zeitlich lokal.
-
httpd mod_unique_id: Host-IP(32) + PID(32) + Zeit(32) + Sequenz(16) + Thread-ID(32).
- Pro: Deterministisch.
- Contra: Verwendet 144 Bits.
- Contra: Geht von einer eindeutigen IPv4 für die Schnittstelle des Hostnamens aus.
- Contra: Nicht sortierbare, groß-/kleinschreibungssensitive benutzerdefinierte Darstellung - base64 mit einem benutzerdefinierten Alphabet.
- Contra: Harte Grenze von 65535 IDs pro Sekunde pro PID - kleine Toleranz für Uhrenschritte.
Entwicklung
Nach dem Auschecken des Repos kann Docker verwendet werden, um die Test-Suite auszuführen:
docker-compose run --rm app make test
Veröffentlichungsprozess
Um Releases an OPM und LuaRocks zu veröffentlichen:
VERSION=x.x.x make release
GitHub
Zusätzliche Konfigurationstipps und Dokumentation für dieses Modul findest du im GitHub-Repository für nginx-module-txid.