Der Kern und das Wesen von OpenResty: Cosocket

Cosocket ist die Basis für verschiedene lua-resty- * nicht blockierende Bibliotheken. Ohne cosocket können Entwickler Lua nicht verwenden, um schnell eine Verbindung zu verschiedenen externen Netzwerkdiensten herzustellen.

Wenn Sie in der frühen OpenResty-Version mit Redis und memcached-Diensten interagieren möchten, müssen Sie C-Module wie das redis2-nginx-Modul, das redis-nginx-Modul und das memc-nginx-Modul verwenden. Im Release-Paket.

Nachdem die Cosocket-Funktion hinzugefügt wurde, wurden sie durch lua-resty-redis und lua-resty-memcached ersetzt, und im Grunde verwendet niemand das C-Modul, um externe Dienste zu verbinden.

Was ist Cosocket?

Cosocket ist ein Eigenname in OpenResty. Es wird durch Kombinieren von Coroutinen und Netzwerk-Sockets auf Englisch gebildet, dh Cosocket = Coroutine + Socket. Daher kann Cosocket als "Coroutine-Socket" übersetzt werden.

Cosocket benötigt nicht nur die Unterstützung von Lua-Coroutine-Funktionen, sondern auch die Unterstützung des sehr wichtigen Ereignismechanismus in Nginx. Durch die Kombination der beiden wird schließlich eine nicht blockierende Netzwerk-E / A erreicht. Darüber hinaus unterstützt cosocket TCP, UDP und Unix Domain Socket.

Rufen Sie in OpenResty eine Cosocket-bezogene Funktion auf. Die interne Implementierung sieht wie folgt aus:

 

Jedes Mal, wenn das Lua-Skript eines Benutzers eine Netzwerkoperation auslöst, wird die Coroutine ausgegeben und wieder aufgenommen.

Wenn es auf Netzwerk-E / A stößt, gibt es die Kontrolle ab, registriert Netzwerkereignisse in der Nginx-Überwachungsliste und übergibt Berechtigungen an Nginx. Wenn ein Nginx-Ereignis die Triggerbedingung erreicht, wird die entsprechende Coroutine aktiviert Fortsetzen.

OpenResty basiert darauf und kapselt und implementiert Verbindungs-, Sende-, Empfangs- und andere Vorgänge, die die aktuelle Cosocket-API bilden. Nehmen Sie als Beispiel die API, die TCP behandelt. Bei der Verarbeitung von UDP und Unix Domain Socket ist die Schnittstelle zu TCP grundsätzlich dieselbe.

Einführung in die Cosocket-API und Anweisungen

Die TCP-bezogene Cosocket-API kann in die folgenden Kategorien unterteilt werden:

  • Objekt erstellen: ngx.socket.tcp.
  • Timeout einstellen: tcpsock: Settimeout und tcpsock: Settimeout.
  • Stellen Sie eine Verbindung her: tcpsock: connect.
  • Daten senden: tcpsock: senden.
  • Daten akzeptieren: tcpsock: empfangen, tcpsock: empfangen und tcpsock: empfangen bis.
  • Verbindungspool: tcpsock: setkeepalive.
  • Schließen Sie die Verbindung: tcpsock: close.

Der Kontext, in dem diese APIs verwendet werden können:

rewrite_by_lua *, access_by_lua *, content_by_lua *, ngx.timer. *, ssl_certificate_by_lua *, ssl_session_fetch_by_lua * _

Aufgrund verschiedener Einschränkungen des Nginx-Kernels ist die Cosocket-API in set_by_lua *, log_by_lua *, header_filter_by_lua * und body_filter_by_lua * nicht verfügbar. Es ist in init_by_lua * und init_worker_by_lua * vorübergehend nicht verfügbar, aber der Nginx-Kernel unterliegt keinen Einschränkungen für diese beiden Phasen.

Darüber hinaus gibt es im Zusammenhang mit diesen APIs 8 Nginx-Anweisungen, die mit lua_socket_ beginnen:

  • lua_socket_connect_timeout: Verbindungszeitlimit, der Standardwert beträgt 60 Sekunden.
  • lua_socket_send_timeout: Sendezeitlimit, der Standardwert beträgt 60 Sekunden.
  • lua_socket_send_lowat: Sendeschwelle (Niedrigwasser), der Standardwert ist 0.
  • lua_socket_read_timeout: Lesezeitlimit, der Standardwert beträgt 60 Sekunden.
  • lua_socket_buffer_size: Die Größe des Pufferbereichs zum Lesen von Daten ist standardmäßig 4k / 8k.
  • lua_socket_pool_size: Größe des Verbindungspools, Standard 30.
  • lua_socket_keepalive_timeout: Leerlaufzeit des Cosocket-Objekts des Verbindungspools, Standard 60 Sekunden
  • lua_socket_log_errors: Gibt an, ob Protokolle aufgezeichnet werden sollen, wenn ein Fehler im Cosocket auftritt. Die Standardeinstellung ist aktiviert.

Einige Befehle haben dieselbe Funktion wie die API, z. B. das Festlegen des Zeitlimits und der Größe des Verbindungspools. Wenn jedoch ein Konflikt zwischen den beiden besteht, ist die Priorität der API höher als die Anweisung, wodurch der durch die Anweisung festgelegte Wert überschrieben wird. Daher wird im Allgemeinen empfohlen, die API zu verwenden, um Einstellungen vorzunehmen, die flexibler sind. 

Anhand eines bestimmten Beispiels erfahren Sie, wie Sie diese Cosocket-API verwenden. Senden Sie eine TCP-Anfrage an eine Website und drucken Sie den zurückgegebenen Inhalt aus:

resty -e 'local sock = ngx.socket.tcp () 
    sock: settimeout (1000) - eine Sekunde timeout 
    local ok, err = sock: connect ("www.baidu.com", 80) 
    wenn nicht ok, dann 
        ngx. say ("Verbindung fehlgeschlagen:", err) 
        return 
    end 
local req_data = "GET / HTTP / 1.1 \ r \ nHost: www.baidu.com \ r \ n \ r \ n" 
lokale Bytes, err = sock: send ( req_data) 
wenn err dann 
    ngx.say ( "nicht bestanden senden:" err) 
    return 
Ende 
lokale Daten, err, partielle = Socke: receive () , 
wenn err dann 
    ngx.say ( "erhalten fehlgeschlagen:" err) 
    return 
Ende 
sock 
: close () ngx.say ("Antwort ist:", Daten) 
'

Analysieren Sie diesen Code:

  • Erstellen Sie zunächst über ngx.socket.tcp () ein TCP-Cosocket-Objekt. Der Name lautet sock.
  • Verwenden Sie dann Settimeout (), um das Timeout auf 1 Sekunde festzulegen. Beachten Sie, dass das Zeitlimit hier nicht zwischen Verbinden und Empfangen unterscheidet, sondern eine einheitliche Einstellung ist.
  • Verwenden Sie dann connect (), um eine Verbindung zu Port 80 der angegebenen Website herzustellen, und beenden Sie das Programm direkt, wenn dies fehlschlägt.
  • Wenn die Verbindung erfolgreich ist, senden Sie die erstellten Daten mit send () und beenden Sie das Programm, wenn die Übertragung fehlschlägt.
  • Wenn die Daten erfolgreich gesendet wurden, verwenden Sie receive (), um die von der Website zurückgegebenen Daten zu empfangen. Hier ist der Standardparameterwert von receive () * l, was bedeutet, dass nur die erste Datenzeile zurückgegeben wird. Wenn der Parameter auf * a gesetzt ist, werden weiterhin Daten empfangen, bis die Verbindung geschlossen wird.
  • Rufen Sie abschließend close () auf, um die Socket-Verbindung aktiv zu schließen.

 

Als nächstes nehmen wir einige Anpassungen an diesem Beispiel vor:

Die erste Aktion legt das Zeitlimit für die drei Aktionen Socket-Verbindung, Senden und Lesen fest.

Die Funktion von Settimeout () besteht darin, die Timeout-Zeit gleichmäßig auf einen Wert zu setzen. Wenn Sie separat festlegen möchten, müssen Sie die Funktion settimeouts () verwenden, z. B. das folgende Schreiben:

Socke: Settimeouts (1000, 2000, 3000)

Zeigt an, dass das Verbindungszeitlimit 1 Sekunde, das Sendezeitlimit 2 Sekunden und das Lesezeitlimit 3 Sekunden beträgt. In den Bibliotheken OpenResty und lua-resty sind die meisten zeitbezogenen API-Parameter in Millisekunden angegeben.

 

Die zweite Aktion besteht darin, Inhalte der angegebenen Größe zu empfangen.

Die Schnittstelle receive () kann eine Datenzeile empfangen oder weiterhin Daten empfangen. Wenn Sie nur 10.000 Daten empfangen möchten, sollten Sie Receiveany () verwenden, um diese Anforderung zu erfüllen

lokale Daten, err, partiell = Socke: Empfang (10240)

In Bezug auf den Empfang gibt es eine weitere sehr häufige Benutzeranforderung, nämlich Daten immer abzurufen, bis sie auf eine bestimmte Zeichenfolge stoßen.

receiveuntil () wird speziell verwendet, um diese Art von Problem zu lösen. Es wird keine Zeichenfolge wie receive () und receiveiveany () zurückgegeben, sondern ein Iterator. Auf diese Weise kann es in der Schleife aufgerufen werden, um die übereinstimmenden Daten in Segmenten zu lesen, und wenn der Lesevorgang abgeschlossen ist, wird null zurückgegeben.

lokaler Leser = Socke: Empfang bis ("\ r \ n"), 
während wahr, 
    lokale Daten, err, partiell = Leser (4), 
    wenn nicht Daten, 
        wenn err, dann 
            ngx.say ("Datenstrom konnte nicht gelesen werden:", err ) 
            break 
        end 
        ngx.say ("read done") 
        break 
    end 
    ngx.say ("read chunk: [", data, "]") 
end

receiveuntil gibt die Daten vor \ r \ n zurück und liest jedes Mal 4 Bytes durch den Iterator.

Die dritte Aktion besteht nicht darin, den Socket direkt zu schließen, sondern ihn in den Verbindungspool zu stellen.

Ohne Verbindungspool muss jedes Mal, wenn eine Anforderung eingeht, eine neue Verbindung erstellt werden, wodurch häufig Cosocket-Objekte erstellt und zerstört werden, was zu unnötigen Leistungsverlusten führt.

Um dieses Problem zu vermeiden, können Sie nach Verwendung eines Cosockets setkeepalive () aufrufen und in den Verbindungspool stellen

lokale ok, err = Socke: setkeepalive (2 * 1000, 100) , 
wenn nicht ok dann 
    ngx.say ( "to set wiederverwendbar ist fehlgeschlagen:" err) 
Ende    

Dieser Code setzt die Leerlaufzeit der Verbindung auf 2 Sekunden und die Größe des Verbindungspools auf 100 Sekunden. Auf diese Weise wird beim Aufrufen der Funktion connect () das Cosocket-Objekt zuerst aus dem Verbindungspool abgerufen.

Bei der Verwendung von Verbindungspools sind zwei Punkte zu beachten:

Erstens können Sie die Verbindung mit dem Fehler nicht in den Verbindungspool aufnehmen. Andernfalls führt das nächste Mal, wenn Sie sie verwenden, zu einem Fehler beim Senden und Empfangen von Daten. Dies ist ein Grund, warum jeder API-Aufruf als erfolgreich bewertet werden muss.

Zweitens müssen wir die Anzahl der Verbindungen herausfinden. Der Verbindungspool befindet sich auf Worker-Ebene, und jeder Worker verfügt über einen eigenen Verbindungspool. Wenn also 10 Worker vorhanden sind und die Größe des Verbindungspools auf 30 festgelegt ist, entspricht dies für den Back-End-Service 300 Verbindungen.

 

Ich denke du magst

Origin www.cnblogs.com/liekkas01/p/12757576.html
Empfohlen
Rangfolge