Objet Python en mémoire persistant pour le serveur nginx / uwsgi

Je doute que cela soit possible, mais voici le problème et la solution proposée (la faisabilité de la solution proposée fait l’object de cette question):

J’ai quelques “données globales” qui doivent être disponibles pour toutes les demandes. Je persiste à transmettre ces données à Riak et à utiliser Redis comme couche de mise en cache pour la vitesse d’access (pour l’instant …). Les données sont divisées en environ 30 segments logiques, chacun d’environ 8 Ko.

Chaque requête est nécessaire pour lire 4 de ces blocs de 8 Ko, ce qui donne 32 Ko de données lues depuis Redis ou Riak. Ceci est en plus de toute donnée spécifique à la requête qui devrait également être lue (ce qui est un peu).

En supposant même 3000 requêtes par seconde (ce n’est pas un serveur live donc je n’ai pas de vrais nombres, mais 3000ps est une hypothèse raisonnable, pourrait être plus), cela signifie 96Kbps de transfert de Redis ou Riak en plus du non déjà -les autres appels importants provenant de la logique de l’application. En outre, Python parsing le JSON de ces objects de 8 Ko 3 000 fois par seconde.

Tout cela – en particulier Python devant à plusieurs resockets désérialiser les données – semble un gaspillage total, et une solution parfaitement élégante serait de simplement mettre en cache les données désérialisées dans un object natif en mémoire en Python , que je peux actualiser régulièrement au fur et à mesure. quand toutes ces données “statiques” deviennent obsolètes. Une fois dans quelques minutes (ou heures), au lieu de 3000 fois par seconde.

Mais je ne sais pas si c’est même possible. Vous auriez besoin d’une application “toujours active” pour mettre en cache toutes les données de sa mémoire. Et je sais que ce n’est pas le cas dans la combinaison nginx + uwsgi + python (par opposition à quelque chose comme node) – les données en mémoire python ne seront PAS persistantes à toutes les demandes , à moins que je ne me trompe.

Malheureusement, c’est un système que j’ai «hérité» et qui ne peut donc pas apporter trop de changements en termes de technologie de base, et je ne connais pas assez la manière dont la combinaison nginx + uwsgi + python fonctionne en termes de démarrage de processus Python Données en mémoire Python – ce qui signifie que je pourrais être terriblement trompé par mon hypothèse ci-dessus!

Donc, des conseils directs pour savoir si cette solution fonctionnerait + des références à des matériaux qui pourraient m’aider à comprendre comment nginx + uwsgi + python fonctionnerait en termes de démarrage de nouveaux processus et d’allocation de mémoire, seraient très utiles.

PS:

  1. J’ai parcouru une partie de la documentation de nginx, uwsgi, etc. mais je n’ai pas encore bien compris les ramifications par cas d’utilisation. J’espère faire des progrès sur ce qui se passe maintenant

  2. Si la chose en mémoire pouvait fonctionner, je jetterais Redis, car je ne cache que les données statiques que j’ai mentionnées ci-dessus. Cela me rend plus attrayant pour un cache Python en mémoire persistant en cours de processus, réduisant une partie mobile du système et au moins QUATRE allers-retours de réseau par demande.

Ce que vous suggérez n’est pas directement réalisable. Étant donné que les nouveaux processus peuvent être générés hors de votre contrôle, il est impossible de conserver les données Python natives en mémoire.

Cependant, il y a plusieurs manières de contourner cela.

Un seul niveau de stockage de la valeur-clé est souvent suffisant. Et parfois, avoir des tampons de taille fixe pour les valeurs (que vous pouvez utiliser directement en tant bytearray str / bytes / bytearray ; tout ce que vous avez besoin de struct ou de sérialiser) est tout ce dont vous avez besoin. Dans ce cas, le cadre de mise en cache intégré à uWSGI prendra en charge tout ce dont vous avez besoin.

Si vous avez besoin d’un contrôle plus précis, vous pouvez voir comment le cache est implémenté par-dessus SharedArea et faire quelque chose de personnalisé. Cependant, je ne recommanderais pas cela. En gros, cela vous donne le même type d’API que vous obtenez avec un fichier, et les seuls avantages réels par rapport à l’utilisation d’un fichier sont que le serveur gère la durée de vie du fichier; cela fonctionne dans tous les langages supportés par uWSGI, même ceux qui n’autorisent pas les fichiers; et il est plus facile de migrer votre cache personnalisé vers un cache dissortingbué (multi-ordinateur) si nécessaire. Je ne pense pas que l’une de ces questions vous concerne.

Une autre façon d’obtenir un stockage de valeur clé, mais sans les tampons de taille fixe, est d’utiliser anydbm anydbm de anydbm . La recherche clé-valeur est aussi pythonique que possible: elle ressemble à un dict , sauf qu’elle est sauvegardée sur une firebase database BDB (ou similaire) sur disque, mise en cache en mémoire, au lieu d’être stockée dans une firebase database. table de hachage de mémoire.

Si vous avez besoin de manipuler quelques autres types simples – tout ce qui est extrêmement rapide à décompresser, comme int s-vous voudrez peut-être envisager la mise en shelve .

Si votre structure est suffisamment rigide, vous pouvez utiliser la firebase database de valeurs-clés pour le niveau supérieur, mais accéder aux valeurs via un ctypes.Structure ou de / serialize avec struct . Mais généralement, si vous pouvez le faire, vous pouvez également éliminer le niveau supérieur, à quel point tout votre système n’est qu’une grande Structure ou un grand Array .

À ce stade, vous pouvez simplement utiliser un fichier simple pour le stockage, soit le ctypes (pour ctypes ), soit l’ open et le read (pour struct ).

Vous pouvez également utiliser multiprocessing objects ctypes partagés du ctypes pour accéder directement à votre Structure depuis une zone de mémoire partagée.

En attendant, si vous n’avez pas besoin de toutes les données de cache en permanence, juste des morceaux de temps en temps, c’est exactement ce que sont les bases de données. Encore une fois, anydbm , etc. peut être tout ce dont vous avez besoin, mais si vous avez une structure complexe, établissez un diagramme ER, transformez-le en un ensemble de tables et utilisez quelque chose comme MySQL.

Vous n’avez rien dit sur l’écriture de ces données, est-ce statique? Dans ce cas, la solution est simple et je n’ai aucune idée de ce qui se passe avec toutes les réponses “ce n’est pas faisable”.

Les employés d’Uwsgi sont des applications toujours actives. Les données sont donc toujours conservées entre les requêtes. Tout ce que vous avez à faire est de stocker des éléments dans une variable globale, c’est tout. Et rappelez-vous que c’est par employé, et que les travailleurs redémarrent de temps en temps, vous avez donc besoin de stratégies de chargement / invalidation appropriées.

Si les données sont mises à jour très rarement (ce qui est rarement suffisant pour redémarrer le serveur), vous pouvez économiser encore plus. Créez simplement les objects lors de la construction de l’application. De cette manière, ils seront créés exactement une fois, puis tous les travailleurs sépareront le maître et réutiliseront les mêmes données. Bien sûr, il est copié sur écriture, donc si vous le mettez à jour, vous perdrez les avantages de la mémoire (la même chose se produira si python décide de compacter sa mémoire lors d’une exécution gc, ce n’est donc pas prévisible).

Je ne l’ai jamais vraiment essayé moi-même, mais pourriez-vous utiliser SharedArea de uWSGI pour accomplir ce que vous recherchez?

“À ma connaissance, les données en mémoire python ne seront pas conservées dans toutes les requêtes, à moins que je ne me trompe complètement.”

tu te trompes.

L’intérêt de l’utilisation de uwsgi sur, par exemple, le mécanisme CGI consiste à conserver les données sur les threads et à enregistrer la surcharge de l’initialisation pour chaque appel. vous devez définir des processes = 1 dans votre fichier .ini ou, selon la configuration de uwsgi, il peut lancer plus d’un processus de travail en votre nom. connectez l’ env et cherchez 'wsgi.multiprocess': False et 'wsgi.multithread': True , et tous les threads uwsgi.core pour le travailleur individuel doivent afficher les mêmes données.

Vous pouvez également voir combien de processus de travail et de threads “core” sous chacun de vous, en utilisant le stats-server .

c’est pourquoi uwsgi fournit des fonctions de lock et de unlock pour manipuler les magasins de données par plusieurs threads.

Vous pouvez facilement tester cela en ajoutant une route /status dans votre application qui vide simplement une représentation json de votre object de données global et l’affiche de temps en temps après les actions qui mettent à jour le magasin.