2011-08-03 32 views
28

¿Cuál es una forma limpia y elegante de hacer comunicación entre procesos entre dos procesos de python diferentes? Actualmente uso canalizaciones con nombre en el sistema operativo, pero se siente un poco hacky. Reescribí mis cosas con los servicios dbus, que funcionó, pero parece que al ejecutar el código de forma remota a través de una sesión SSH ahora intenta inicializar X11, lo que parece completamente innecesario para las cosas que quiero hacer (no están relacionadas con la GUI). Así que tal vez dbus es un poco demasiado pesado. Estaba a punto de rediseñar usando sockets, pero parece bastante bajo, así que pensé que podría haber un módulo de nivel superior que pudiera importar y usar del que simplemente no sé el nombre, y pensé que debería preguntarle SO primero ..comunicación entre procesos en python

Mi requisito es poder ejecutar python foo.py y hacer que ese proceso lo haga allí, como un daemon, y poder enviarle mensajes con python foo.py --bar. La última llamada debería simplemente enviar un mensaje al proceso existente y terminar, posiblemente con un código de retorno 0 por éxito u otro por falla (por lo que será necesaria alguna comunicación bidireccional).

Respuesta

25

Nah, zeromq es el camino a seguir. Delicioso, ¿verdad?

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') 
+0

gracias, muy bueno – wim

+1

¿se puede modificar esto para que funcione en un entorno de subprocesos múltiples? Tengo varios hilos de trabajo conectados ahora, y parece estar causando fallas de afirmación en el código de zmq c – wim

+0

También puede ajustar zmq con pizco: https://pizco.readthedocs.io/en/latest/ –

3

Utilizaría enchufes; La comunicación local se optimizó en gran medida, por lo que no debería tener problemas de rendimiento y le da la capacidad de distribuir su aplicación a diferentes nodos físicos si surgieran las necesidades.

Con respecto al enfoque de "bajo nivel", tiene razón. Pero siempre puedes usar un contenedor de nivel superior dependiendo de tus necesidades. XMLRPC podría ser un buen candidato, pero tal vez sea excesivo para la tarea que está tratando de realizar.

Twisted ofrece algunas buenas implementaciones de protocolos simples, tales como LineReceiver (para mensajes basados ​​en líneas simples) o el más elegante AMP (que era, por cierto, standardized and implemented in different languages).

+0

No son s ¿Los paquetes son relativamente lentos para uso local en Windows? (¿O estoy pensando en todo el IPC local?) Por lo tanto, podría depender del entorno de despliegue de PO. Y si está utilizando Twisted, también tienen 'ProcessProtocol', que podría valer la pena mirar. – detly

+0

ProcessProtocol resuelve una problemática completamente diferente y no se puede usar para comunicarse con un proceso ya en ejecución. – GaretJax

+0

Con respecto a Windows, puede que tenga razón; Tengo solo una experiencia muy limitada en Windows. Con respecto a "todos los IPC locales", si encuentro mis referencias para lo indicado anteriormente, agregaré el enlace. – GaretJax

1

Usaría conectores, pero uso Twisted para darle un poco de abstracción y facilitar las cosas. Their Simple Echo Client/Server example es un buen lugar para comenzar.

Simplemente tendría que combinar los archivos y crear instancias y ejecutar el cliente o servidor dependiendo de los argumentos pasados.

61

El multiprocessing library proporciona listeners and clients que las tomas de envoltura y le permiten pasar objetos de Python arbitrarias.

Su servidor podría escuchar para recibir los objetos de 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() 

Su cliente podría enviar comandos como objetos:

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() 
+0

no se usa en el código, creo que deberías eliminar la importación. –

+0

eliminado la importación – vsekhar

+1

En python 3 la autenticación debe ser una cadena de bytes: authkey = b'secret contraseña ' –

7

Desde mi experiencia, rpyc es, con mucho, la forma más sencilla y elegante a hazlo.

(sé que esto es una cuestión de edad, pero acabo topé con ella ..)

1

Salida una biblioteca/servidor multiplataforma llamado RabbitMQ. Puede ser demasiado pesado para la comunicación de dos procesos, pero si necesita una comunicación multiproceso o de código múltiple (con varios medios diferentes, por ejemplo, de uno a muchos, colas, etc.), es una buena opción.

Requisitos:

$ pip install pika 
$ pip install bson # for sending binary content 
$ sudo apt-get rabbitmq-server # ubuntu, see rabbitmq installation instructions for other platforms 

Publisher (envía los datos):

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

Suscriptor (recibe datos, puede ser múltiple):

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

ejemplos basados ​​en https://www.rabbitmq.com/tutorials/tutorial-two-python.html

Cuestiones relacionadas