Comunicare con un demone Python in esecuzione
Domanda
Ho scritto una piccola applicazione Python che funziona come un demone. Utilizza thread e code.
Sto cercando approcci generali per modificare questa applicazione in modo da poter comunicare con essa mentre è in esecuzione. Principalmente vorrei essere in grado di monitorare la sua salute.
In breve, vorrei poter fare qualcosa del genere:
python application.py start # launches the daemon
In seguito, vorrei poter venire e fare qualcosa del tipo:
python application.py check_queue_size # return info from the daemonized process
Per essere chiari, non ho alcun problema ad implementare la sintassi ispirata a Django. Quello che non ho idea di come fare è inviare segnali al processo daemonizzato (start) o come scrivere il demone per gestire e rispondere a tali segnali.
Come ho detto sopra, sto cercando approcci generali. L'unico che posso vedere in questo momento è dire al demone di registrare costantemente tutto ciò che potrebbe essere necessario in un file, ma spero che ci sia un modo meno disordinato per farlo.
AGGIORNAMENTO: Caspita, molte ottime risposte. Grazie mille. Penso che esaminerò entrambi gli approcci di Pyro e web.py/Werkzeug, dal momento che Twisted è un po 'più di quello che voglio mordere a questo punto. La prossima sfida concettuale, suppongo, è come parlare con i miei thread di lavoro senza riagganciarli.
Grazie ancora.
Soluzione
Che ne dici di far funzionare un server http?
Sembra folle, ma con un semplice server web per amministrare il tuo il server richiede solo poche righe usando web.py
Puoi anche prendere in considerazione la creazione di una pipe unix.
Altri suggerimenti
Ancora un altro approccio: usa Pyro (oggetti remoti Python).
Pyro consente sostanzialmente di pubblicare istanze di oggetti Python come servizi che possono essere chiamati in remoto. Ho usato Pyro per lo scopo esatto che descrivi e ho trovato che funziona molto bene.
Per impostazione predefinita, un demone del server Pyro accetta connessioni da qualsiasi luogo. Per limitare ciò, utilizzare un validatore di connessione (consultare la documentazione) o fornire host = '127.0.0.1'
al costruttore Daemon
per ascoltare solo le connessioni locali.
Esempio di codice tratto dalla documentazione di Pyro:
Server
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 altro progetto simile è RPyC . Non ho provato RPyC.
Usa werkzeug e fai in modo che il tuo demone includa un server WSGI basato su HTTP.
Il tuo demone ha una raccolta di piccole app WSGI per rispondere con le informazioni sullo stato.
Il tuo client usa semplicemente urllib2 per effettuare richieste POST o GET a localhost: somePort. Il client e il server devono concordare il numero di porta (e l'URL).
Questo è molto semplice da implementare e molto scalabile. L'aggiunta di nuovi comandi è un esercizio banale.
Nota che il tuo demone non deve rispondere in HTML (spesso è semplice). I nostri demoni rispondono alle richieste WSGI con oggetti di stato con codifica JSON.
Userei attorcigliato con una pipa denominata o semplicemente aprire una presa. Dai un'occhiata al server e al client echo esempi . Dovresti modificare il server echo per verificare la presenza di una stringa passata dal client e quindi rispondere con qualsiasi informazione richiesta.
A causa dei problemi di threading di Python avrai problemi a rispondere alle richieste di informazioni mentre continuerai a fare qualunque cosa il demone debba fare comunque. Le tecniche asincrone o il fork di altri processi sono la tua unica vera opzione.
# 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()
il client può essere scritto usando xmlrpclib, controllare il codice di esempio qui .
Supponendo che tu sia sotto * nix, puoi inviare segnali a un programma in esecuzione con kill
da una shell (e analoghi in molti altri ambienti). Per gestirli da Python, controlla il modulo signal .
Potresti associarlo a Pyro ( http://pythonhosted.org/Pyro4/ ) the Python Oggetto remoto. Ti consente di accedere in remoto agli oggetti Python. È facile da implementare, ha bassi costi generali e non è invasivo come Twisted.