53 votes

Communiquer avec un démon python en cours d'exécution

J'ai écrit une petite application Python qui fonctionne comme un démon. Elle utilise le threading et les files d'attente.

Je suis à la recherche d'approches générales pour modifier cette application afin de pouvoir communiquer avec elle pendant qu'elle est en cours d'exécution. J'aimerais surtout être en mesure de surveiller sa santé.

En bref, j'aimerais pouvoir faire quelque chose comme ceci :

python application.py start  # launches the daemon

Plus tard, j'aimerais pouvoir venir et faire quelque chose comme.. :

python application.py check_queue_size  # return info from the daemonized process

Pour être clair, je n'ai aucun problème à mettre en œuvre la syntaxe inspirée de Django. Ce que je ne sais pas du tout, c'est comment envoyer des signaux au processus démonisé (start), ou comment écrire le démon pour gérer et répondre à de tels signaux.

Comme je l'ai dit plus haut, je cherche des approches générales. La seule que je vois pour l'instant est de dire au démon d'enregistrer constamment dans un fichier tout ce qui pourrait être nécessaire, mais j'espère qu'il y a une façon moins désordonnée de procéder.

UPDATE : Wow, beaucoup de bonnes réponses. Merci beaucoup. Je pense que je vais examiner à la fois Pyro et les approches de web.py/Werkzeug, car Twisted est un peu plus que ce que je veux mordre à ce stade. Le prochain défi conceptuel, je suppose, est de savoir comment parler à mes threads de travail sans les suspendre.

Merci encore.

36voto

codeape Points 38576

Une autre approche encore : utiliser Pyro (objets de remoting Python).

Pyro vous permet essentiellement de publier des instances d'objets Python en tant que services pouvant être appelés à distance. J'ai utilisé Pyro dans le but exact que vous décrivez, et j'ai trouvé qu'il fonctionnait très bien.

Par défaut, un démon serveur Pyro accepte des connexions de partout. Pour limiter cela, utilisez un validateur de connexion (voir la documentation), ou fournissez le fichier host='127.0.0.1' à la Daemon pour n'écouter que les connexions locales.

Exemple de code tiré de la documentation de Pyro :

Serveur

import Pyro.core

class JokeGen(Pyro.core.ObjBase):
        def \_\_init\_\_(self):
                Pyro.core.ObjBase.\_\_init\_\_(self)
        def joke(self, name):
                return "Sorry "+name+", I don't know any jokes."

Pyro.core.initServer()
daemon=Pyro.core.Daemon()
uri=daemon.connect(JokeGen(),"jokegen")

print "The daemon runs on port:",daemon.port
print "The object's uri is:",uri

daemon.requestLoop()

Client

import Pyro.core

# you have to change the URI below to match your own host/port.
jokes = Pyro.core.getProxyForURI("PYROLOC://localhost:7766/jokegen")

print jokes.joke("Irmen")

Un autre projet similaire est RPyC . Je n'ai pas essayé RPyC.

0 votes

Je pense que Pyro est totalement sur-engineering pour ça. Il donne trop de pouvoir et de liberté, oui, mais il introduit beaucoup de nouvelles erreurs possibles dans le logiciel. Je n'utiliserais pyro que si la communication entre différents serveurs a lieu, jamais localement. Vous avez toujours de meilleurs choix comme les signaux unix, qui sont beaucoup plus robustes dans un environnement local. Selon la complexité de la logique de votre application, cela peut être insuffisant. Si vous avez besoin d'un court man-in-the-middle (ce qui est ce que Pyro proxy est sous tout) je recommanderais un serveur http pour recevoir/envoyer des demandes. Mais c'est un choix personnel.

0 votes

Quoi qu'il en soit, les bons vieux sockets d'écoute TCP sont tout à fait suffisants pour cela, mais, comme toujours, il y a des problèmes de sécurité. Je suis en train de faire un daemon complexe, et je suis tenté d'utiliser Pyro (car le projet utilise Pyro pour créer un pool de travailleurs à distance multi-serveurs, donc la plupart des choses sont écrites dans le style Pyro et les sérialiseurs sont déjà écrits. La classe principale elle-même hérite de threads et fonctionne à la manière des démons, et elle est déjà appelée avec Pyro a et enregistrée dans nameserver, et malgré tout cela, je suis réticent à l'utiliser comme point d'entrée de mon démon local.

0 votes

Est 7766 numéro de port par défaut ?

18voto

fulmicoton Points 5389

Et si on lui faisait exécuter un serveur http ?

Cela semble fou, mais faire fonctionner un simple serveur web pour administrer votre serveur ne nécessite que quelques lignes en utilisant web.py.

Vous pouvez également envisager de créer un pipe unix.

0 votes

Aussi +1 pour l'interface HTTP. Un script python peut analyser les options de la ligne de commande et envoyer des commandes XMLRPC à un serveur HTTP interne.

1 votes

+1 : HTTP. Intégrer une petite application WSGI dans le démon pour répondre aux demandes.

3 votes

(et @VanGale et @S.Lott) quelqu'un pourrait-il fournir une référence/un exemple pour l'exécution d'un serveur http dans le but de recevoir des commandes comme celles décrites par le PO ? J'ai besoin de le faire, mais j'aimerais avoir un peu plus de détails.

16voto

S.Lott Points 207588

Utilisez werkzeug et faire en sorte que votre démon comprenne un serveur WSGI basé sur HTTP.

Votre démon possède une collection de petites applications WSGI pour répondre avec des informations d'état.

Votre client utilise simplement urllib2 pour effectuer des requêtes POST ou GET vers localhost:somePort. Votre client et votre serveur doivent se mettre d'accord sur le numéro de port (et les URL).

Cette méthode est très simple à mettre en œuvre et très évolutive. L'ajout de nouvelles commandes est un exercice trivial.

Notez que votre démon n'a pas besoin de répondre en HTML (c'est souvent simple, cependant). Nos démons répondent aux requêtes WSGI avec des objets d'état codés en JSON.

0 votes

Comment obtenir les réponses aux erreurs et leurs traces lors de l'utilisation de l'application werkzeug ?

9voto

MrEvil Points 1046

Je l'utiliserais torsadé avec un tuyau nommé ou simplement ouvrir une douille. Jetez un oeil au serveur et au client d'écho exemples . Il faudrait modifier le serveur d'écho pour qu'il vérifie la présence d'une chaîne passée par le client et qu'il réponde avec les informations demandées.

En raison des problèmes de threading de Python, vous aurez du mal à répondre aux demandes d'information tout en continuant à faire ce que le démon est censé faire de toute façon. Les techniques asynchrones ou la bifurcation d'autres processus sont votre seule véritable option.

1 votes

+1 pour Twisted, voir aussi twisted.manhole qui fournit une interface telnet directement dans l'interpréteur en cours d'exécution : twistedmatrix.com/projets/core/documentation/howto/telnet.html

0 votes

"...] vous allez avoir du mal à répondre aux demandes d'information tout en continuant à faire ce que le démon est censé faire de toute façon" Je trouve cette affirmation sans fondement. Si vous parlez de la GIL, elle n'empêche pas du tout ce type de concurence.

0 votes

Si l'interprète a acquis la GIL et qu'il effectue une opération de longue durée, il est évident qu'il empêchera l'autre thread d'être desservi. Le fait est qu'un profane ne peut pas facilement prédire quand la GIL entrera en jeu et causera des problèmes de threading.

7voto

Badri Points 427
# your server

from twisted.web import xmlrpc, server
from twisted.internet import reactor

class MyServer(xmlrpc.XMLRPC):

    def xmlrpc_monitor(self, params):        
        return server_related_info

if __name__ == '__main__':
    r = MyServer()
    reactor.listenTCP(8080, Server.Site(r))
    reactor.run()

Le client peut être écrit en utilisant xmlrpclib, vérifiez le code d'exemple. aquí .

0 votes

Vous pouvez facilement écrire le serveur et le client sans dépendre de twisted, mais c'est une bonne réponse.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X