如何在Python与其他应用之间传递和共享数据?
简单来说,我需要一个Python脚本,它可以根据来自不同应用程序的指令做一些事情。目前还不清楚这些应用程序具体是什么,可能是另一个Python程序、MATLAB应用,或者是LAMP配置。指令发送的频率很低,大概每小时几次。
问题是 - 我的Python脚本接收这些指令的最佳方式是什么?怎么让这些应用程序知道我已经收到了指令?
现在,我尝试用一个简单的.txt文件。应用程序会把指令写入这个文件,Python脚本会读取这些指令,执行相应的操作,然后把指令从文件中删除。
我不喜欢这个方法有两个原因:
1) 如果Python正在读取或写入文件,而应用程序又发送了一个新指令,那会发生什么?
2) 这个方法太复杂了,不能保证稳定和有效。
2 个回答
Python从早期开始就有一个非常好用的PyZMQ绑定,可以与ZeroMQ配合使用。
MATLAB也可以直接使用ZeroMQ,适合你的多对多通信需求。
让我从几个关键原则的角度来谈谈,这些原则在其他软件工程的“产品”和“技术栈”中并不常见:
[1] ZeroMQ首先是一个非常强大的概念,而不仅仅是一段代码或一个DIY工具包。
[2] ZeroMQ对任何专业项目的最大优势在于使用真正的可扩展正式通信模式进行端到端的通信,而不是仅仅在于编写代码或“修改”已有的内部结构。
[3] ZeroMQ团队做得非常出色,帮助用户避免重复造轮子(在内部),让用户能够利用那些经过ZeroMQ专家、支持者和团队成员精心打磨和测试的知识,从而保持在最有效率的工作状态。
说完这些原则,我建议你花一些时间阅读Peter Hintjens关于ZeroMQ的书(也有PDF版本)。这是一个很好的起点,可以帮助你更全面地理解这个概念。
接下来,你只需要写几行代码,就能使用这个世界上最强大的工具(相信我,这听起来很大胆,但实际上没有很多真正的替代品可以与ZeroMQ相比……当然,ZeroMQ的共同创始人Martin Sustrik的[nanomsg]是一个例子,如果你需要更高的速度或更低的延迟,但上面提到的关键原则在这里依然适用)。
在外汇高频交易的基础设施中使用ZeroMQ来协调Python、MQL4和AI/ML系统只是一个小例子,在这里微秒很重要,纳秒则能决定排队的差异……
希望你对ZeroMQ库的兴趣会不断增长,并且你能像其他许多用户一样,从这个优秀的工具中获得巨大的收益,无论是PUB/SUB、PAIR/PAIR还是REQ/REP这些正式模式,都能很好地满足你在MATLAB/Python/*异构多方/多主机项目中的通信需求。
集成模式
有几种集成模式可以用来实现这个目的。
消息传递可以通过发送约定好的消息来解耦,其他的细节则留给具体的平台处理,这不会影响到其他参与者。
另一种方法可以是共享数据存储——一方将请求存储在这里,另一方则取出并处理这些请求。
使用ZeroMQ的消息传递集成模式
ZeroMQ是一个库,可以实现非常轻量级的消息传递,而不需要运行一个大型的消息应用程序。
你所描述的情况可以这样解决:
- 一个Python脚本在循环中运行,等待请求来执行某些操作。
- 任何能够发出ZeroMQ请求的客户端都可以请求一个动作并获得响应。
由于客户端可能使用多种语言,你应该使用一些跨平台的序列化方式,结果缓冲区或者更好的选择是JSON字符串。
ZeroMQ支持多种语言的库,所以你应该能够进行通信。
有一个很棒的ZeroMQ指南,提供了清晰的概念解释,并展示了多种语言的实现。
在规划通信基础设施时,我建议你先让你的Python脚本绑定到某个TCP端口(作为“固定”部分),然后让你的客户端连接到这个端口。
Python脚本会使用REP套接字(用于“回复”),而你其他语言的客户端则使用REQ套接字。
以下示例来自zguide中的大量示例,这些示例是为多种语言编写的。
示例Python服务器代码(hwserver.py
):
import zmq
import time
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://*:5555")
while True:
# Wait for next request from client
message = socket.recv()
print("Received request: %s" % message)
# Do some 'work'
time.sleep(1)
# Send reply back to client
socket.send(b"World")
示例PHP客户端(hwclient.php
):
<?php
/*
* Hello World client
* Connects REQ socket to tcp://localhost:5555
* Sends "Hello" to server, expects "World" back
* @author Ian Barber <ian(dot)barber(at)gmail(dot)com>
*/
$context = new ZMQContext();
// Socket to talk to server
echo "Connecting to hello world server...\n";
$requester = new ZMQSocket($context, ZMQ::SOCKET_REQ);
$requester->connect("tcp://localhost:5555");
for ($request_nbr = 0; $request_nbr != 10; $request_nbr++) {
printf ("Sending request %d...\n", $request_nbr);
$requester->send("Hello");
$reply = $requester->recv();
printf ("Received reply %d: [%s]\n", $request_nbr, $reply);
}
通过共享数据存储进行集成
共享文件或数据库也可以实现进程之间的通信。问题在于资源的锁定,确保同一任务不会被多个工作者处理等,这使得问题变得复杂。
结论
有多种模式和多种平台可以实现。
- 数据库——你需要设置并让它运行。对于一些小任务来说,这可能过于复杂。
- 共享文件——多个进程共享文件可能会发生冲突——如果多个进程尝试写入,可能会出现冲突。
- 通过TCP或UDP套接字进行消息传递——这项工作比较繁琐,但实际上技术上并不简单,因为有很多边界情况需要解决(比如一方掉线后该怎么办,如何重新连接……)。
- 通过Web服务进行消息传递——你可以设置一个Web服务器,接受来自客户端的请求并在服务器上处理。这对于快速任务来说相对简单,但如果任务超过30秒,你可能会失败,需要转为后台处理——使用某种消息传递方式。
- 中型和大型消息系统(如Celery与RabbitMQ、Redis等)——这些系统有很好的工具,但在很多情况下可能显得过于复杂。
- 通过ZeroMQ进行消息传递——这是我最喜欢的方式——你可以用几行代码设置它,并且运行时不会有太多问题,而且它能解决诸如重新连接等问题。例如,查看分布式锁的完整工作示例。