Pools de connexion pour les fonctions sans serveur et les services backend

Pools de connexion pour les fonctions sans serveur et les services backend

Lors de l’écriture d’une application serveur qui se connecte à une base de données, vous devez souvent faire face à des pools de connexion, avec des répercussions problématiques si vous ignorez le problème trop longtemps. Alors, plongeons dans le problème et explorons comment les pools de connexion le résolvent.

Trop de connections

Les applications serveur partagent une exigence commune : elles doivent répondre à des requêtes indépendantes provenant de plusieurs clients. Une application serveur écrite naïvement qui utilise Redis (ou toute autre base de données) ouvrira une nouvelle connexion à Redis pour chaque requête. Malheureusement, cette approche n’est pas évolutive, car vous ne pouvez ouvrir qu’un nombre limité de connexions en même temps avant que tout n’explose. Ouvrir et fermer des connexions n’est pas non plus bon marché : ni pour l’application ni pour la base de données.

La bonne nouvelle est que ce problème est entièrement résoluble en modifiant le code de votre application. La mauvaise nouvelle est qu’il n’est pas toujours facile de le faire soi-même, et c’est là que des modèles tels que les pools de connexions peuvent aider.

Fonctions sans serveur

Les fonctions sans serveur sont un ajout relativement récent aux offres cloud, mais à bien des égards, elles ressemblent aux anciennes scripts CGI: un petit extrait de code qui est invoqué par requête. Si chaque invocation est indépendante de toutes les autres, cela signifie-t-il qu’il est impossible de partager des connexions ? Pas exactement.

Normalement, lorsqu’une application appelle une fonction sans serveur, le processus reste actif pendant un certain temps avant d’être arrêté, au cas où d’autres demandes arriveraient. Tant que le processus reste actif, il peut maintenir une connexion de longue durée avec Redis, mais c’est à vous de l’implémenter correctement.

En général, il est facile de créer une connexion persistante, il vous suffit de l’instancier en dehors du corps de la fonction principale. Voici un exemple avec AWS Lambda en utilisant JavaScript :

Notez que vous devrez télécharger ce script dans une archive Zip qui inclut node_redisun client Redis pour Node.js (la documentation AWS explique cela plus en détail).

Le même concept peut être appliqué à d’autres langages et offres de cloud Function-as-a-Service (FaaS) (Fonctions Google Cloud, Fonctions Microsoft Azureetc).

Dans le cas de JavaScript, le client n’offre pas de pool de connexion en raison de la nature monothread de Node.js. Dans l’exemple ci-dessus, nous essayions de réutiliser la même connexion sur plusieurs requêtes, mais si vous deviez utiliser une fonction dans Go, ou dans un autre langage qui a une simultanéité multithread, le client aurait également besoin d’un verrouillage de connexion schéma, tel qu’un pool de connexions.

Comment fonctionnent les pools de connexion ?

Le principe de base est simple : un client qui implémente un pool de connexion ouvre n connexions à la base de données, puis dispose d’un mécanisme pour marquer les connexions comme « disponibles » ou « en cours d’utilisation » et utiliser uniquement les connexions gratuites. De nombreux pools de connexions fonctionnent comme des remplacements sur place pour les connexions uniques, donc appeler .relier() prélèvera une connexion du pool (c’est-à-dire la marquera comme “en cours d’utilisation” et renverra la vraie connexion à l’appelant), tandis que .proche() va simplement le remettre (sans le fermer).

Si vous utilisez un langage multithread comme Go, assurez-vous de choisir un client qui prend en charge le regroupement de connexions. Go-redis est un bon choix de ce point de vue, car vous pouvez lire dans la documentation.

Certains clients vous permettent également d’envoyer des commandes directement sans retirer au préalable une connexion du pool. Bien que pratique, il y a certaines choses à garder à l’esprit lorsque vous utilisez une piscine de cette façon (plus à ce sujet ci-dessous).

Prestations de service

Avec les fonctions sans serveur, toute l’architecture de l’application est extrêmement simple : c’est juste une fonction. Cependant, lorsqu’il s’agit de services «serveurs», le partage d’une connexion devient plus fastidieux lorsque la simultanéité est impliquée.

Une simple connexion socket ne peut pas être directement utilisée par plus d’un thread, car un certain degré de coordination est nécessaire pour éviter d’envoyer des bits et des morceaux de plusieurs requêtes en même temps, ce qui entraînerait un mélange incompréhensible pour le récepteur.

Dans de tels cas, les pools de connexions sont un bon moyen de donner l’impression que chaque sous-composant est le seul à utiliser une connexion, mais même les pools de connexions ne peuvent pas complètement supprimer tous les détails de la gestion des connexions.

Empêcher les connexions de fuir

Lorsqu’une connexion est extraite du pool, votre code doit s’assurer que la connexion est finalement rétablie. Les pools de connexions implémentent une limite supérieure sur le nombre de connexions pouvant être ouvertes à tout moment (rappelez-vous que la limitation du nombre total de connexions fait partie de l’objectif), de sorte que les connexions qui fuient finiront par bloquer votre service lors de la dernière .relier() se bloque pour toujours, refusant d’ouvrir une nouvelle connexion et attendant en vain qu’une connexion existante revienne à la piscine.

Parfois, du code qui n’a pas été conçu pour durer longtemps est incorporé dans un projet plus important et commence à perdre des connexions. Pour éviter les fuites, il suffit de s’assurer de .proche() la connexion une fois que vous n’en avez plus besoin, mais ce n’est pas toujours facile à mettre en œuvre dans la pratique, en particulier dans les grands projets compliqués.

Voyons un bon moyen d’utiliser un pool de connexions et d’assurer un nettoyage correct en Python.

Un exemple Python : aio-redis

Pour vous montrer un exemple de code, je vais utiliser aio-redisun client Redis pour Python qui prend en charge l’asynchronie. Avec aio-redis, il est possible d’utiliser un pool de connexion directement sans arracher une connexion au préalable, comme illustré ici :

Comme mentionné précédemment, cela fonctionne bien pour une utilisation simple, mais il est préférable d’extraire explicitement une connexion du pool dans certaines situations, en particulier lorsqu’une opération prend beaucoup de temps, comme dans le blocage des opérations sur les flux, les listes, les ensembles triés ou ATTENDEZ.

Blocage des opérations

Alors que les commandes Redis ont tendance à être très rapides, certaines commandes sont conçues pour être bloquantes, ce qui signifie qu’elles ne renverront pas de réponse tant que certaines conditions ne seront pas remplies. Par exemple, bloquer les lectures sur Streams (XREAD) attendra que de nouvelles entrées entrent dans le flux lorsqu’il est utilisé avec le BLOQUER option (sans elle, XREAD reviendrait immédiatement avec un jeu de résultats vide). Gardez à l’esprit que ces opérations bloquent le client, pas le serveur. Redis pourra toujours répondre aux commandes envoyées via d’autres connexions.

Ces types de commandes sont un problème pour le modèle d’utilisation que nous avons montré précédemment, car aio-redis ne sait pas pendant combien de temps une commande donnée s’exécutera et pourrait décider de mettre une nouvelle commande en file d’attente sur une connexion occupée par une commande bloquante. Cela signifie que dans l’exemple précédent, s’il y avait une autre fonction asynchrone utilisant le pool pour effectuer un blocage XREADnotre POSITIONNER et INCRBY les commandes peuvent avoir pris un temps étonnamment long à se terminer, ou peuvent même expirer.

Dans ces cas, vous devez extraire explicitement une connexion du pool et vous assurer également de la renvoyer une fois que vous avez terminé. Python aide avec la dernière partie avec une fonctionnalité de langage appelée gestionnaires de contexte, à laquelle vous pouvez accéder à l’aide d’un avec bloquer. Le bloc gestionnaire de contexte est créé autour d’une ressource qu’il faut toujours nettoyer (connexions, descripteurs de fichiers). À la fin du bloc, que nous sortions avec succès ou en levant une exception, le gestionnaire de contexte déclenche la procédure de nettoyage appropriée, qui dans notre cas consiste à renvoyer la connexion au pool, comme illustré ici :

(Si vous connaissez les gestionnaires de contexte et asyncio, vous remarquerez peut-être que le avec piscine en attente… partie est un peu étrange, car cela est généralement exprimé comme asynchrone avec le pool … . C’est une petite bizarrerie de l’implémentation d’aio-redis, donc tout fonctionne toujours comme prévu. Tu peux trouver plus d’informations sur ce problème ici.)

MULTI/EXEC transactions

Voici un autre cas particulier : MULTI/EXEC transactions. Ne vous méprenez pas, les transactions ne sont pas des opérations de blocage du client, mais elles font un usage particulier de la connexion.

Quand tu envoies MULTI la connexion change d’état et Redis commence à mettre en file d’attente toutes les commandes, au lieu de les exécuter immédiatement. Lorsqu’une commande est mise en file d’attente avec succès (c’est-à-dire, il ne contient pas d’erreurs de formatage flagrantes), Redis répond par D’ACCORD. Cela signifie que la connexion n’est pas littéralement bloquée, mais elle ne peut pas vraiment être utilisée pour multiplexer les commandes de plusieurs sous-parties du programme qui ne sont pas conscientes du fait qu’une transaction est en cours.

Une fois que vous appelez EXEC ou JETERl’ensemble de la transaction réussira ou échouera respectivement, et la connexion reviendra à un état normal.

Pour cette raison, de nombreux clients ont des objets dédiés qui représentent une transaction. Normalement, les objets de transaction n’envoient même pas les commandes tant que vous n’avez pas conclu la transaction. Cela améliore les performances sans changer la sémantique, puisque, comme mentionné précédemment, les commandes ne seront mises en file d’attente par Redis que jusqu’à ce que la transaction soit finalisée :

La gestion des connexions ne peut être ignorée

La gestion des connexions est une partie importante de toute application côté serveur, car il s’agit souvent d’un chemin sensible, compte tenu de la relation un-à-plusieurs entre les serveurs et les clients. La promesse d’une évolutivité infinie dans les fonctions sans serveur peut causer des problèmes lorsque vous ne gérez pas correctement les connexions, mais heureusement, la solution est facile à mettre en œuvre.

Pour les architectures plus complexes, les pools de connexions vous permettent de penser à la gestion des connexions uniquement au niveau local (sous-composant), mais vous ne pouvez pas complètement renoncer à la gestion des connexions, en particulier lorsque vous effectuez des opérations qui utilisent spécialement la connexion, telles que les transactions. ou bloquer des opérations.

Development Source

Related Posts

RLEC 4.2.1 apporte des contrôles granulaires à la haute disponibilité et aux performances

RLEC 4.2.1 apporte des contrôles granulaires à la haute disponibilité et aux performances

Comment HolidayMe utilise Redis Enterprise comme base de données principale

Comment HolidayMe utilise Redis Enterprise comme base de données principale

Annonce de RedisGears 1.0 : un moteur sans serveur pour Redis

Annonce de RedisGears 1.0 : un moteur sans serveur pour Redis

Clés Redis dans la RAM |  Redis

Clés Redis dans la RAM | Redis

No Comment

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *