简体   繁体   English

Python中的进程间通信

[英]Interprocess communication in Python

What is a good way to communicate between two separate Python runtimes?在两个独立的 Python 运行时之间进行通信的好方法是什么? Thing's I've tried:我试过的事情:

  • reading/writing on named pipes eg os.mkfifo (feels hacky)在命名管道上读/写,例如os.mkfifo (感觉很os.mkfifo
  • dbus services (worked on desktop, but too heavyweight for headless) dbus服务(在桌面上工作,但对于 headless 来说太重了)
  • sockets (seems too low-level, surely there's a higher level module to use?)套接字(似乎太低级了,肯定有更高级别的模块可以使用吗?)

My basic requirement is to be able to run python listen.py and have that process just doing it's thing there, like a daemon, able to receive messages from python client.py --bar .我的基本要求是能够运行python listen.py并让该进程在那里做它的事情,就像一个守护进程,能够从python client.py --bar接收消息。 The client call should just send a message to the existing process and terminate, with return code 0 for success or nonzero for failure (ie some two-way communication will be required)客户端调用应该只向现有进程发送一条消息并终止,成功返回代码0或失败返回非零(即需要一些双向通信)

The multiprocessing library provides listeners and clients that wrap sockets and allow you to pass arbitrary python objects. multiprocessing提供了封装套接字的侦听器和客户端,并允许您传递任意的 Python 对象。

Your server could listen to receive python objects:您的服务器可以侦听接收 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()

Your client could send commands as objects:您的客户端可以将命令作为对象发送:

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

Nah, zeromq is the way to go.不, zeromq是要走的路。 Delicious, isn't it?很好吃,不是吗?

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

From my experience, rpyc is by far the simplest and most elegant way to go about it.根据我的经验, rpyc是迄今为止最简单、最优雅的方法。

(I know this is an old question, but I've just stumbled upon it..) (我知道这是一个老问题,但我只是偶然发现了它......)

Based on @vsekhar's answer, here is a Python 3 version with more details and multiple connections:根据@vsekhar 的回答,这是一个 Python 3 版本,其中包含更多详细信息和多个连接:

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

Client客户

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

I would use sockets;我会使用套接字; local communication was strongly optimized, so you shouldn't have performance problems and it gives you the ability to distribute your application to different physical nodes if the needs should arise.本地通信经过了高度优化,因此您不应该遇到性能问题,并且可以在需要时将应用程序分发到不同的物理节点。

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. XMLRPC可能是一个不错的候选者,但对于您要执行的任务来说,它可能有点过头了。

Twisted offers some good protocol simple implementations, such as LineReceiver (for simple line based messages) or the more elegant AMP (which was, by the way, standardized and implemented in different languages ). Twisted提供了一些很好的协议简单实现,例如LineReceiver (用于简单的基于行的消息)或更优雅的 AMP(顺便说一下,它被标准化并以不同的语言实现)。

Check out a cross-platform library/server called RabbitMQ.查看名为 RabbitMQ 的跨平台库/服务器。 Might be too heavy for two-process communication, but if you need multi-process or multi-codebase communication (with various different means, eg one-to-many, queues, etc), it is a good option.对于双进程通信来说可能太繁重了,但是如果您需要多进程或多代码库通信(使用各种不同的方式,例如一对多、队列等),这是一个不错的选择。

Requirements:要求:

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

Publisher (sends data):发布者(发送数据):

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

Subscriber (receives data, can be 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()

Examples based on https://www.rabbitmq.com/tutorials/tutorial-two-python.html基于https://www.rabbitmq.com/tutorials/tutorial-two-python.html 的示例

I would use sockets, but use Twisted to give you some abstraction, and to make things easy.我会使用套接字,但使用 Twisted 为您提供一些抽象,并使事情变得简单。 Their Simple Echo Client / Server example is a good place to start. 他们的 Simple Echo Client/Server 示例是一个很好的起点。

You would just have to combine the files and instantiate and run either the client or server depending on the passed argument(s).您只需要组合文件并根据传递的参数实例化和运行客户端或服务器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM