Python 中的进程间通信

在两个独立的 Python 运行时之间进行通信的好方法是什么:

  • 在命名管道(例如 os.mkfifo)上读/写
  • dbus 服务(在台式机上工作,但对于无头机来说过于重量级)
  • 套接字(似乎太低级了; 肯定有更高级的模块可以使用?)

我的基本要求是能够像守护进程一样运行 python listen.py,能够接收来自 python client.py的消息。客户机只需向现有进程发送一条消息并终止,返回代码 0表示成功,非零表示失败(即需要双向通信)

104684 次浏览

我会使用套接字; 本地通信经过了强有力的优化,因此不会出现性能问题,而且如果需要的话,它还可以将应用程序分发到不同的物理节点。

With regard to the "low-level" approach, you're right. But you can always use an higher-level wrapper depending on your needs. XMLRPC could be a good candidate, but it is maybe overkill for the task you're trying to perform.

Twsted 提供了一些很好的协议简单实现,比如 线路接收器(用于基于简单行的消息)或者更优雅的 AMP (顺便说一句,它是 以不同语言标准化和实施)。

I would use sockets, but use Twisted to give you some abstraction, and to make things easy. 他们的 Simple Echo Client/Server 示例 is a good place to start.

您只需要组合这些文件并实例化,然后根据传递的参数运行客户机或服务器。

不,zeromq才是正确的选择,很美味,不是吗?

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

multiprocessing提供了包装套接字并允许您传递任意 python 对象的 听众和客户

您的服务器可以监听接收 python 对象:

from multiprocessing.connection import Listener


address = ('localhost', 6000)     # family is deduced to be 'AF_INET'
listener = Listener(address, authkey=b'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()

您的客户端可以将命令作为对象发送:

from multiprocessing.connection import Client


address = ('localhost', 6000)
conn = Client(address, authkey=b'secret password')
conn.send('close')
# can also send arbitrary objects:
# conn.send(['a', 2.5, None, int, sum])
conn.close()

根据我的经验,到目前为止,rpyc是最简单和最优雅的方法。

查看一个名为 RabbitMQ 的跨平台库/服务器。对于两进程通信可能过于繁重,但是如果您需要多进程或多代码库通信(使用各种不同的方法,例如一对多、队列等) ,那么它是一个不错的选择。

Requirements:

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

发布者(发送数据) :

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

订阅服务器(接收数据,可以是多个) :

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

基于 https://www.rabbitmq.com/tutorials/tutorial-two-python.html的实例

Based on @vsekhar's answer, here is a Python 3 version with more details and multiple connections:

Server

from multiprocessing.connection import Listener


listener = Listener(('localhost', 6000), authkey=b'secret password')
running = True
while running:
conn = listener.accept()
print('connection accepted from', listener.last_accepted)
while True:
msg = conn.recv()
print(msg)
if msg == 'close connection':
conn.close()
break
if msg == 'close server':
conn.close()
running = False
break
listener.close()

客户

from multiprocessing.connection import Client
import time


# Client 1
conn = Client(('localhost', 6000), authkey=b'secret password')
conn.send('foo')
time.sleep(1)
conn.send('close connection')
conn.close()


time.sleep(1)


# Client 2
conn = Client(('localhost', 6000), authkey=b'secret password')
conn.send('bar')
conn.send('close server')
conn.close()