Может ли 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 с текущей информацией о процессе.