web-dev-qa-db-fra.com

Communication interprocessus en Python

Quelle est une façon propre et élégante de faire une communication interprocessus entre deux processus différents python? J'utilise actuellement des canaux nommés dans le système d'exploitation, mais cela semble un peu hacky. J'ai réécrit mes trucs avec dbus services, qui ont fonctionné, mais il semble qu'en exécutant le code à distance via une session SSH, il essaie maintenant d'initialiser X11, ce qui semble complètement inutile pour les choses que je veux faire (elles ne sont pas liées à l'interface graphique). Alors peut-être dbus est un peu trop lourd. J'étais sur le point de repenser à nouveau à l'aide de sockets, mais il semble assez bas, 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 je pensais que je devrais demander sur SO d'abord ..

Mon exigence est de pouvoir exécuter python foo.py et avoir ce processus juste en train de faire sa chose, comme un démon, et pouvoir lui envoyer des messages avec python foo.py --bar. Ce dernier appel doit simplement envoyer un message au processus existant et se termine, éventuellement avec un code retour 0 pour le succès ou autre pour l'échec (donc une communication bidirectionnelle sera nécessaire).

46
wim

La bibliothèque multiprocessing fournit écouteurs et clients qui enveloppent les sockets et vous permettent de passer des objets arbitraires python.

Votre serveur peut é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 peut envoyer des commandes sous forme d'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()
85
vsekhar

Non, zeromq est la voie à suivre. Délicieux, non?

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')
36
zeekay

D'après mon expérience, rpyc est de loin la manière la plus simple et la plus élégante de s'y prendre.

(Je sais que c'est une vieille question, mais je suis juste tombé dessus ..)

7
shx2

J'utiliserais des sockets; la communication locale a été fortement optimisée, vous ne devriez donc pas avoir de problèmes de performances et cela vous donne la possibilité de distribuer votre application sur différents nœuds physiques si les besoins se présentent.

En ce qui concerne l'approche "bas niveau", vous avez raison. Mais vous pouvez toujours utiliser un wrapper de niveau supérieur en fonction de vos besoins. XMLRPC pourrait être un bon candidat, mais c'est peut-être exagéré pour la tâche que vous essayez d'effectuer.

Twisted offre de bonnes implémentations simples de protocole, telles que LineReceiver (pour les messages simples basés sur la ligne) ou l'AMP plus élégant (qui était, soit dit en passant, normalisé) et implémenté dans différentes langues ).

3
GaretJax

J'utiliserais des sockets, mais utiliser Twisted pour vous donner une certaine abstraction et pour vous faciliter la tâche. Leur exemple Simple Echo Client/Server est un bon point de départ.

Il vous suffirait de combiner les fichiers et d'instancier et d'exécuter le client ou le serveur en fonction des arguments passés.

1
jozzas

Découvrez une bibliothèque/serveur multiplateforme appelée RabbitMQ. Cela peut être trop lourd pour une communication à deux processus, mais si vous avez besoin d'une communication multi-processus ou multi-code (avec différents moyens, par exemple un-à-plusieurs, des 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

É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

1
Mika Vatanen