Может ли python script знать, что работает еще один экземпляр того же script... и затем поговорить с ним?

Я хотел бы запретить запуск нескольких экземпляров одной и той же длинной командной строки python script, и я хотел бы, чтобы новый экземпляр мог отправлять данные в исходный экземпляр до новый экземпляр совершает самоубийство. Как я могу сделать это кросс-платформенным способом?

В частности, я хотел бы включить следующее поведение:

  • foo.py "запускается из командной строки, и он будет работать в течение длительного времени - дней или недель, пока компьютер не перезагрузится или родительский процесс не убьет его.
  • каждые несколько минут снова запускается тот же script, но с различными параметрами командной строки
  • при запуске script должен увидеть, запущены ли какие-либо другие экземпляры.
  • Если выполняются другие экземпляры, то экземпляр # 2 должен отправить свои параметры командной строки в экземпляр # 1, а затем экземпляр # 2 должен выйти.
  • instance # 1, если он получает параметры командной строки из другого script, должен развернуть новый поток и (используя параметры командной строки, отправленные на шаге выше), начнет выполнять работу, которая прошла экземпляр # 2 выполнить.

Итак, я ищу две вещи: как программа python знает, что другой экземпляр сам запущен, а затем как одна из команд командной строки python взаимодействует с другой?

Чтобы сделать это более сложным, тот же самый script должен работать как в Windows, так и в Linux, поэтому в идеале решение будет использовать только стандартную библиотеку Python, а не любые вызовы, специфичные для ОС. Хотя если мне нужно иметь кодировку Windows и кодировку * nix (и большой оператор if в моем коде, чтобы выбрать тот или иной), это нормально, если решение "того же кода" невозможно.

Я понимаю, что я мог бы, вероятно, разработать подход, основанный на файлах (например, экземпляр # 1 просматривает каталог для изменений, и каждый экземпляр бросает файл в этот каталог, когда он хочет выполнять работу), но я немного обеспокоен уборкой эти файлы после неправильного завершения работы машины. В идеале я бы мог использовать решение в памяти. Но снова я гибкий, если подход с постоянным файлом - единственный способ сделать это, я открыт для этого варианта.

Более подробно: я пытаюсь это сделать, потому что наши серверы используют средство мониторинга, которое поддерживает запуск сценариев python для сбора данных мониторинга (например, результаты запроса базы данных или вызова веб-службы), который затем отслеживает инструмент мониторинга использовать. Некоторые из этих сценариев очень дороги для запуска, но дешево для запуска после запуска (например, создание соединения с БД и выполнение запроса). Поэтому мы решили сохранить их в бесконечном цикле до тех пор, пока родительский процесс не убьет их.

Это отлично работает, но на более крупных серверах может работать 100 экземпляров одного и того же script, даже если они собирают данные только каждые 20 минут каждый. Это приводит к хаосу с ОЗУ, ограничениями на соединение с БД и т.д. Мы хотим перейти от 100 процессов с 1 потоком к одному процессу с 100 потоками, каждый из которых выполняет работу, ранее выполнявшуюся script.

Но изменение способа запуска скриптов средствами мониторинга невозможно. Нам нужно поддерживать одинаковый вызов (запустить процесс с разными параметрами командной строки), но изменить скрипты, чтобы распознать, что другой активен, и "новый" script отправить свои рабочие инструкции (из параметров командной строки ) к "старому" script.

Кстати, это не то, что я хочу сделать на основе one- script. Вместо этого я хочу скомпоновать это поведение в библиотеку, которую могут использовать многие авторы script. Моя цель - включить авторов script для написания простых однопоточных скриптов, которые не знают о проблемах с несколькими экземплярами и обрабатывать многопоточное и однострочное под крышкой.

Ответ 1

Подход Alex Martelli к настройке канала связи является подходящим. Я бы использовал метод multiprocessing.connection.Listener для создания слушателя по вашему выбору. Документация по адресу: http://docs.python.org/library/multiprocessing.html#multiprocessing-listeners-clients

Вместо использования AF_INET (сокетов) вы можете выбрать AF_UNIX для Linux и AF_PIPE для Windows. Надеюсь, небольшое "если" не повредит.

Изменить. Я думаю, что пример не повредит. Тем не менее, это основной.

#!/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)

Это работает на OS X, поэтому ему нужно протестировать как с Linux, так и после подстановки нужного адреса Windows. Много предостережений существует из точки безопасности, главным из которых является то, что conn.recv распаковывает свои данные, поэтому вы почти всегда лучше с recv_bytes.

Ответ 2

Общий подход заключается в том, чтобы script при запуске настраивал канал связи таким образом, чтобы гарантировать, что он является исключительным (другие попытки настроить один и тот же канал не предсказуемым образом), так что дополнительные экземпляры script может обнаружить, что первый работает и разговаривает с ним.

Ваши требования к межплатформенной функциональности настоятельно указывают на использование сокета в качестве рассматриваемого канала связи: вы можете назначить "хорошо известный порт", который зарезервирован для вашего script, скажем 12345, и открыть сокет на этом порту прослушивание только localhost (127.0.0.1). Если попытка открыть этот сокет не удалась, потому что соответствующий порт "взят", вы можете подключиться к этому номеру порта, а это позволит вам общаться с существующим script.

Если вы не знакомы с программированием сокетов, здесь есть хороший HOWTO doc здесь. Вы также можете взглянуть на соответствующую главу в Python в двух словах (я, конечно, склонен к этому, -).

Ответ 3

Возможно, попробуйте использовать сокеты для связи?

Ответ 4

Похоже, что ваш лучший выбор - придерживаться файла pid, но он не только содержит идентификатор процесса - пусть он также включает номер порта, который прослушивает предыдущий экземпляр. Поэтому при запуске проверьте файл pid и, если он есть, посмотрите, работает ли процесс с этим идентификатором, если вы отправляете свои данные на него и завершаете перезапись файла pid с текущей информацией о процессе.