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).

35
demandé sur jww 2011-08-03 05:57:51

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()
70
répondu vsekhar 2016-03-14 00:36:51

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')
34
répondu zeekay 2011-08-03 03:39:45

D'après mon expérience, rpyc est de loin la façon la plus simple et élégante de procéder.

(je sais que c'est une vieille question, mais je viens de tomber dessus..)

7
répondu shx2 2014-04-08 04:58:51

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 ).

3
répondu GaretJax 2011-08-03 02:01:08

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.

1
répondu jozzas 2011-08-03 03:24:43

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

1
répondu Mika Vatanen 2016-10-17 18:40:02