python脚本可以知道同一个脚本的另一个实例正在运行...然后与它交谈吗?

Jus*_*ant 11 python command-line multithreading ipc interprocess

我想防止同一个长时间运行的python命令行脚本的多个实例同时运行,我希望新实例能够在新实例自杀之前将数据发送到原始实例.我怎样才能以跨平台的方式做到这一点?

具体来说,我想启用以下行为:

  1. " foo.py"从命令行启动,它将保持运行很长时间 - 几天或几周,直到机器重新启动或父进程终止它.
  2. 每隔几分钟再次启动相同的脚本,但使用不同的命令行参数
  3. 启动时,脚本应查看是否有其他实例正在运行.
  4. 如果其他实例正在运行,则实例#2应将其命令行参数发送到实例#1,然后实例#2应退出.
  5. 实例#1,如果它从另一个脚本接收命令行参数,则应该启动一个新线程并(使用上面步骤中发送的命令行参数)开始执行实例#2将要执行的工作.

所以我正在寻找两件事:python程序如何知道自己的另一个实例正在运行,然后一个python命令行程序如何与另一个进行通信?

使这更复杂,相同的脚本需要在Windows和Linux上运行,因此理想情况下,该解决方案将仅使用Python标准库而不是任何特定于操作系统的调用.虽然如果我需要一个Windows代码路径和一个*nix代码路径(以及if我的代码中的一个大语句来选择其中一个),那么如果无法实现"相同代码"解决方案就没问题.

我意识到我可能会找到一个基于文件的方法(例如,实例#1监视一个目录进行更改,每个实例在它想要工作时将文件放入该目录)但是我有点担心清理这些文件在非优雅的机器关闭后.理想情况下,我可以使用内存解决方案.但是我再次灵活,如果基于持久文件的方法是唯一的方法,我会对这个选项持开放态度.

更多细节:我正在尝试这样做,因为我们的服务器正在使用监视工具,该工具支持运行python脚本来收集监视数据(例如,数据库查询或Web服务调用的结果),监视工具随后将其编入索引供以后使用.其中一些脚本启动起来非常昂贵,但在启动后运行起来很便宜(例如,建立数据库连接而不是运行查询).所以我们选择让它们在无限循环中运行,直到父进程杀死它们.

这很好用,但是在较大的服务器上,同一个脚本的100个实例可能正在运行,即使它们每个仅每20分钟收集一次数据.这会对RAM,数据库连接限制等造成严重破坏.我们希望将100个进程从1个线程切换到100个线程的进程,每个进程执行以前一个脚本正在执行的工作.

但是,无法更改监视工具调用脚本的方式.我们需要保持调用相同(使用不同的命令行参数启动进程),但是更改脚本以识别另一个是活动的,并让"new"脚本发送其工作指令(从命令行参数)到"旧"剧本.

顺便说一句,这不是我想要在一个脚本基础上做的事情.相反,我想将这种行为打包到一个许多脚本作者可以利用的库中 - 我的目标是使脚本作者能够编写不知道多实例问题的简单的单线程脚本,并处理多线程和单一实例的封面.

Muh*_*uri 10

Alex Martelli建立通信渠道的方法是合适的.我会使用multiprocessing.connection.Listener来创建一个你选择的监听器.文档位于:http: //docs.python.org/library/multiprocessing.html#multiprocessing-listeners-clients

您可以选择使用AF_UNIX for Linux和AF_PIPE for Windows,而不是使用AF_INET(套接字).希望小"如果"不会受到伤害.

编辑:我想一个例子不会受到伤害.不过,这是一个基本的.

#!/usr/bin/env python

from multiprocessing.connection import Listener, Client
import socket
from array import array
from sys import argv

def myloop(address):
    try:
        listener = Listener(*address)
        conn = listener.accept()
        serve(conn)
    except socket.error, e:
        conn = Client(*address)
        conn.send('this is a client')
        conn.send('close')

def serve(conn):
    while True:
        msg = conn.recv()
        if msg.upper() == 'CLOSE':
            break
        print msg
    conn.close()

if __name__ == '__main__':
    address = ('/tmp/testipc', 'AF_UNIX')
    myloop(address)
Run Code Online (Sandbox Code Playgroud)

这适用于OS X,因此需要使用Linux和(在替换正确的地址之后)Windows进行测试.从安全角度来看存在很多警告,主要的一点是conn.recv取消了它的数据,因此你几乎总是更好地使用recv_bytes.


Ale*_*lli 9

一般方法是让脚本在启动时以保证独占的方式建立通信通道(其他尝试以可预测的方式设置相同的通道失败),以便脚本的其他实例可以检测到第一个人正在跑步与之交谈.

您对跨平台功能的要求强烈指向使用套接字作为有问题的通信通道:您可以指定为您的脚本保留的"众所周知的端口",例如12345,并在该端口上打开仅收听localhost的套接字( 127.0.0.1).如果尝试打开该套接字失败,因为有问题的端口被"取走",那么您可以改为连接到该端口号,这样您就可以与现有脚本进行通信.

如果您不熟悉套接字编程,那么这里有一个很好的HOWTO文档.您还可以在Nutshell中查看Python中的相关章节(我当然偏向于那个;-).