La communication Interprocess en Python
Qu'est-ce qu'une façon propre et élégante de faire la communication entre deux processus python différents? J'utilise actuellement des pipes nommées dans L'OS, mais ça semble un peu hacky. J'ai réécrit mes trucs avec les services dbus
, ce qui a fonctionné, mais il semble que lorsque j'exécute le code à distance à travers une session SSH il essaie maintenant d'initialiser X11 ce qui semble complètement inutile pour les choses que je veux faire (ils ne sont pas liés à GUI). Peut-être que dbus
est un peu trop lourd. J'ai été sur le point de redessiner en utilisant des sockets, mais il semble assez bas niveau donc j'ai pensé qu'il pourrait y avoir un module de niveau supérieur que je pourrais importer et utiliser dont je ne connais tout simplement pas le nom, et j'ai pensé que je devrais demander sur le premier..
mon exigence est d'être capable d'exécuter python foo.py
et d'avoir ce processus qui fait juste son truc là, comme un démon, et d'être capable d'envoyer des messages avec python foo.py --bar
. Le dernier appel devrait simplement envoyer un message au processus existant et se termine, peut-être avec un code de retour 0
pour le succès ou autre pour l'échec (donc une communication bidirectionnelle sera nécessaire).
6 réponses
le multiprocessing
bibliothèque fournit auditeurs et clients qui enveloppent les sockets et vous permettent de passer des objets Python arbitraires.
votre serveur pouvait écouter pour recevoir des objets python:
from multiprocessing.connection import Listener
address = ('localhost', 6000) # family is deduced to be 'AF_INET'
listener = Listener(address, authkey='secret password')
conn = listener.accept()
print 'connection accepted from', listener.last_accepted
while True:
msg = conn.recv()
# do something with msg
if msg == 'close':
conn.close()
break
listener.close()
votre client pourrait envoyer des commandes comme objets:
from multiprocessing.connection import Client
address = ('localhost', 6000)
conn = Client(address, authkey='secret password')
conn.send('close')
# can also send arbitrary objects:
# conn.send(['a', 2.5, None, int, sum])
conn.close()
Nan, zeromq est le chemin à parcourir. Délicieux, n'est-ce pas?
import argparse
import zmq
parser = argparse.ArgumentParser(description='zeromq server/client')
parser.add_argument('--bar')
args = parser.parse_args()
if args.bar:
# client
context = zmq.Context()
socket = context.socket(zmq.REQ)
socket.connect('tcp://127.0.0.1:5555')
socket.send(args.bar)
msg = socket.recv()
print msg
else:
# server
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind('tcp://127.0.0.1:5555')
while True:
msg = socket.recv()
if msg == 'zeromq':
socket.send('ah ha!')
else:
socket.send('...nah')
j'utiliserais des sockets; la communication locale a été fortement optimisée, donc vous ne devriez pas avoir de problèmes de performance et il vous donne la possibilité de distribuer votre application à différents noeuds physiques si les besoins se présentent.
en ce qui concerne l'approche" de bas niveau", vous avez raison. Mais vous pouvez toujours utiliser une enveloppe de plus haut niveau en fonction de vos besoins. XMLRPC pourrait être un bon candidat, mais c'est peut-être exagéré pour la tâche que vous êtes en train de s'exécuter.
Twisted offre de bonnes implémentations de protocole simple, comme LineReceiver (pour des messages simples basés sur la ligne) ou L'AMP (qui était, soit dit en passant, standardisé et implémenté dans des langues différentes ).
j'utiliserais des sockets, mais utilisez Twisted pour vous donner un peu d'abstraction, et pour rendre les choses faciles. leur exemple simple de Client / Serveur Echo est un bon endroit pour commencer.
il vous suffit de combiner les fichiers et instancier et d'exécuter soit le client soit le serveur en fonction des arguments passés.
consultez une bibliothèque/serveur multiplateformes appelé RabbitMQ. Peut-être trop lourd pour une communication bi-processus, mais si vous avez besoin d'une communication multi-processus ou multi-codebase (avec différents moyens, par exemple un-à-plusieurs, files d'attente, etc), c'est une bonne option.
exigences:
$ pip install pika
$ pip install bson # for sending binary content
$ sudo apt-get rabbitmq-server # ubuntu, see rabbitmq installation instructions for other platforms
de l'Éditeur (envoie des données):
import pika, time, bson, os
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
channel.exchange_declare(exchange='logs', type='fanout')
i = 0
while True:
data = {'msg': 'Hello %s' % i, b'data': os.urandom(2), 'some': bytes(bytearray(b'\x00\x0F\x98\x24'))}
channel.basic_publish(exchange='logs', routing_key='', body=bson.dumps(data))
print("Sent", data)
i = i + 1
time.sleep(1)
connection.close()
abonné (reçoit des données, peut être multiple):
import pika, bson
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.exchange_declare(exchange='logs', type='fanout')
result = channel.queue_declare(exclusive=True)
queue_name = result.method.queue
channel.queue_bind(exchange='logs', queue=queue_name)
def callback(ch, method, properties, body):
data = bson.loads(body)
print("Received", data)
channel.basic_consume(callback, queue=queue_name, no_ack=True)
channel.start_consuming()
exemples basés sur https://www.rabbitmq.com/tutorials/tutorial-two-python.html