Le cœur et l'essence d'OpenResty: cosocket

Cosocket est la base de diverses bibliothèques non bloquantes lua-resty- * Sans cosocket, les développeurs ne peuvent pas utiliser Lua pour se connecter rapidement à divers services réseau externes.

Dans la première version d'OpenResty, si vous souhaitez interagir avec Redis et les services memcached, vous devez utiliser des modules C tels que redis2-nginx-module, redis-nginx-module et memc-nginx-module. Ces modules sont toujours dans OpenResty. Dans le package de version.

Une fois la fonction cosocket ajoutée, elles ont été remplacées par lua-resty-redis et lua-resty-memcached, et fondamentalement, personne n'utilise le module C pour connecter des services externes.

Qu'est-ce que cosocket

Cosocket est un nom propre dans OpenResty. Il est formé en combinant coroutines et sockets réseau en anglais, c'est-à-dire cosocket = coroutine + socket. Par conséquent, cosocket peut être traduit par "socket coroutine".

Cosocket a besoin non seulement de la prise en charge des fonctionnalités de la coroutine Lua, mais également de la prise en charge du mécanisme d'événement très important de Nginx. De plus, cosocket prend en charge TCP, UDP et Socket de domaine Unix.

Appelez une fonction liée au cosocket dans OpenResty, l'implémentation interne est comme l'image suivante:

 

Chaque fois que le script Lua d'un utilisateur déclenche une opération réseau, il y aura un rendement et une reprise de la coroutine.

Lorsqu'il rencontre des E / S réseau, il abandonne le contrôle, enregistre les événements réseau dans la liste de surveillance Nginx et remet les autorisations à Nginx; lorsqu'un événement Nginx atteint la condition de déclenchement, il réveille la coroutine correspondante Reprendre.

OpenResty est basé sur cela, encapsulant et implémentant des opérations de connexion, d'envoi, de réception et d'autres, formant l'API cosocket actuelle. Prenons l'exemple de l'API qui gère TCP. En gérant UDP et Unix Domain Socket, l'interface avec TCP est fondamentalement la même.

Introduction à l'API cosocket et instructions

L'API de cosocket liée à TCP peut être divisée dans les catégories suivantes:

  • Créez un objet: ngx.socket.tcp.
  • Définissez le délai d'expiration: tcpsock: settimeout et tcpsock: settimeouts.
  • Établissez une connexion: tcpsock: connect.
  • Envoyer des données: tcpsock: envoyer.
  • Accepter les données: tcpsock: recevoir, tcpsock: recevoir et tcpsock: recevoir jusqu'à ce que.
  • Pool de connexions: tcpsock: setkeepalive.
  • Fermez la connexion: tcpsock: fermez.

Le contexte dans lequel ces API peuvent être utilisées:

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

En raison de diverses limitations du noyau Nginx, l'API cosocket n'est pas disponible dans set_by_lua *, log_by_lua *, header_filter_by_lua * et body_filter_by_lua *. Il est temporairement indisponible dans init_by_lua * et init_worker_by_lua *, mais le noyau Nginx n'a aucune restriction sur ces deux étapes.

De plus, concernant ces API, il y a 8 instructions Nginx commençant par lua_socket_:

  • lua_socket_connect_timeout: délai d'expiration de la connexion, la valeur par défaut est 60 secondes.
  • lua_socket_send_timeout: Envoi du timeout, la valeur par défaut est 60 secondes.
  • lua_socket_send_lowat: seuil d'envoi (bas niveau), la valeur par défaut est 0.
  • lua_socket_read_timeout: délai de lecture, la valeur par défaut est 60 secondes.
  • lua_socket_buffer_size: La taille de la zone tampon pour la lecture des données, la valeur par défaut est 4k / 8k.
  • lua_socket_pool_size: taille du pool de connexions, par défaut 30.
  • lua_socket_keepalive_timeout: la durée d'inactivité de l'objet cosocket du pool de connexions, la valeur par défaut est 60 secondes.
  • lua_socket_log_errors: indique s'il faut enregistrer les journaux lorsqu'une erreur se produit dans le cosocket. La valeur par défaut est activée.

Certaines commandes ont la même fonction que l'API, comme la définition du délai d'expiration et la taille du pool de connexions. Cependant, en cas de conflit entre les deux, la priorité de l'API est supérieure à l'instruction, ce qui remplacera la valeur définie par l'instruction. Par conséquent, d'une manière générale, il est recommandé d'utiliser l'API pour définir les paramètres, qui seront plus flexibles. 

À travers un exemple spécifique, pour comprendre comment utiliser ces API cosocket. Envoyez une demande TCP à un site Web et imprimez le contenu retourné:

resty -e 'local sock = ngx.socket.tcp () 
    sock: settimeout (1000) - une seconde timeout 
    local ok, err = sock: connect ("www.baidu.com", 80) 
    si pas ok alors 
        ngx. say ("échec de connexion:", err) 
        return 
    end 
local req_data = "GET / HTTP / 1.1 \ r \ nHost: www.baidu.com \ r \ n \ r \ n" 
octets locaux, err = sock: send ( req_data) 
if err then 
    ngx.say ("failed to send:", err) 
    return 
end 
local data, err, partial = sock: receive () 
if err then 
    ngx.say ("failed to receive:", err) 
    return 
end 
sock: close () 
ngx.say ("la réponse est:", données) 
'

Analysez ce code:

  • Tout d'abord, via ngx.socket.tcp (), créez un objet cosocket TCP, le nom est sock.
  • Ensuite, utilisez settimeout () pour définir le délai d'expiration sur 1 seconde. Notez que le délai d'attente ici ne fait pas de distinction entre la connexion et la réception, il s'agit d'un paramètre unifié.
  • Ensuite, utilisez connect () pour vous connecter au port 80 du site Web spécifié et quittez directement s'il échoue.
  • Si la connexion réussit, utilisez send () pour envoyer les données construites et quittez si la transmission échoue.
  • Si les données sont envoyées avec succès, utilisez receive () pour recevoir les données renvoyées par le site Web. Ici, la valeur du paramètre par défaut de receive () est * l, ce qui signifie que seule la première ligne de données est renvoyée; si le paramètre est défini sur * a, il continuera à recevoir des données jusqu'à la fermeture de la connexion;
  • Enfin, appelez close () pour fermer activement la connexion socket.

 

Ensuite, nous apportons quelques ajustements à cet exemple:

La première action définit le délai d'expiration pour les trois actions de connexion de socket, d'envoi et de lecture.

La fonction de settimeout () est de régler uniformément le délai d'expiration sur une valeur. Si vous souhaitez définir séparément, vous devez utiliser la fonction settimeouts (), comme l'écriture suivante:

chaussette: settimeouts (1000, 2000, 3000)

Indique que le délai de connexion est de 1 seconde, le délai d'envoi est de 2 secondes et le délai de lecture est de 3 secondes. Dans les bibliothèques OpenResty et lua-resty, la plupart des paramètres API liés au temps sont en millisecondes.

 

La deuxième action consiste à recevoir du contenu de la taille spécifiée.

L'interface receive () peut recevoir une ligne de données ou continuer à recevoir des données. Si vous ne souhaitez recevoir que 10K de données, vous devez utiliser receiveany (), qui est conçu pour répondre à cette demande

données locales, err, partiel = chaussette: réception (10240)

En ce qui concerne la réception, il existe une autre exigence utilisateur très courante, qui consiste à toujours obtenir des données jusqu'à ce qu'elles rencontrent une chaîne spécifiée.

receiveuntil () est spécifiquement utilisé pour résoudre ce type de problème. Il ne renverra pas une chaîne comme receive () et receiveany (), mais renverra un itérateur. De cette façon, il peut être appelé dans la boucle pour lire les données appariées en segments, et lorsque la lecture est terminée, il retournera nil.

lecteur local = chaussette: receiveuntil ("\ r \ n") 
alors que vrai, faire 
    des données locales, err, partial = reader (4) 
    sinon données alors 
        si err alors 
            ngx.say ("échec de lecture du flux de données:", err ) 
            break 
        end 
        ngx.say ("read done") 
        break 
    end 
    ngx.say ("read chunk: [", data, "]") 
end

receiveuntil retournera les données avant \ r \ n, et lira 4 octets à chaque fois via l'itérateur,

La troisième action n'est pas de fermer directement le socket, mais de le placer dans le pool de connexions.

Sans pool de connexions, chaque fois qu'une demande arrive, une nouvelle connexion doit être créée, ce qui entraînera la création et la destruction fréquentes d'objets cosockets, entraînant des pertes de performances inutiles.

Pour éviter ce problème, après avoir utilisé un cosocket, vous pouvez appeler setkeepalive () et le placer dans le pool de connexions

local ok, err = sock: setkeepalive (2 * 1000, 100) 
sinon, 
    ngx.say ("échec de la définition de réutilisable:", err) 
fin    

Ce code définit le temps d'inactivité de la connexion à 2 secondes et la taille du pool de connexions à 100. De cette façon, lorsque la fonction connect () est appelée, l'objet cosocket sera d'abord obtenu à partir du pool de connexions.

Il y a deux points à noter sur l'utilisation des pools de connexions:

Tout d'abord, vous ne pouvez pas mettre la connexion avec l'erreur dans le pool de connexions, sinon la prochaine fois que vous l'utiliserez, cela entraînera l'échec de l'envoi et de la réception des données. C'est l'une des raisons pour lesquelles chaque appel d'API doit être jugé comme réussi.

Deuxièmement, nous devons déterminer le nombre de connexions. Le pool de connexions est au niveau du travailleur et chaque travailleur a son propre pool de connexions. Donc, s'il y a 10 travailleurs et que la taille du pool de connexions est définie sur 30, alors pour le service principal, il est égal à 300 connexions.

 

Je suppose que tu aimes

Origine www.cnblogs.com/liekkas01/p/12757576.html
conseillé
Classement