¿Puede un script de Python saber que se está ejecutando otra instancia del mismo script … y luego hablar con él?

Me gustaría evitar que se ejecuten al mismo tiempo varias instancias de la misma secuencia de comandos de línea de comandos de Python, y me gustaría que la nueva instancia pueda enviar datos a la instancia original antes de que la nueva instancia se suicide. . ¿Cómo puedo hacer esto de forma multiplataforma?

Específicamente, me gustaría habilitar el siguiente comportamiento:

  1. foo.py ” se inicia desde la línea de comandos y permanecerá en ejecución durante un largo período de tiempo, días o semanas, hasta que la máquina se reinicie o el proceso principal lo mate.
  2. cada pocos minutos se vuelve a ejecutar el mismo script, pero con diferentes parámetros de línea de comandos
  3. cuando se inicia, la secuencia de comandos debería ver si se están ejecutando otras instancias.
  4. si se están ejecutando otras instancias, entonces la instancia # 2 debería enviar sus parámetros de línea de comandos a la instancia # 1, y luego la instancia # 2 debería salir.
  5. la instancia # 1, si recibe los parámetros de la línea de comandos de otra secuencia de comandos, debe girar un nuevo hilo y (usando los parámetros de la línea de comandos enviados en el paso anterior) comenzar a realizar el trabajo que la instancia # 2 iba a realizar.

Así que estoy buscando dos cosas: ¿cómo puede saber un progtwig de Python que se está ejecutando otra instancia de sí mismo, y luego cómo puede un progtwig de línea de comandos de Python comunicarse con otro?

Para hacer esto más complicado, el mismo script debe ejecutarse tanto en Windows como en Linux, por lo que lo ideal sería que la solución usara solo la biblioteca estándar de Python y no las llamadas específicas del sistema operativo. Aunque si necesito tener una ruta de código de Windows y una ruta de código * nix (y una gran statement if en mi código para elegir una o la otra), está bien si no es posible una solución con “el mismo código”.

Me doy cuenta de que probablemente podría elaborar un enfoque basado en archivos (por ejemplo, la instancia # 1 observa los cambios en un directorio y cada instancia coloca un archivo en ese directorio cuando quiere trabajar) pero estoy un poco preocupado por la limpieza de esos archivos. después de un apagado de la máquina no elegante. Lo ideal sería poder usar una solución en memoria. Pero nuevamente soy flexible, si la única forma de hacerlo es un enfoque basado en archivos persistentes, estoy abierto a esa opción.

Más detalles: estoy intentando hacer esto porque nuestros servidores están usando una herramienta de monitoreo que admite la ejecución de scripts de Python para recostackr datos de monitoreo (por ejemplo, resultados de una consulta de base de datos o llamada de servicio web) que la herramienta de monitoreo luego indexa para su uso posterior. Algunos de estos scripts son muy caros de iniciar, pero su ejecución es más económica (p. Ej., Hacer una conexión de base de datos o ejecutar una consulta). Así que hemos elegido mantenerlos funcionando en un bucle infinito hasta que el proceso principal los mate.

Esto funciona muy bien, pero en servidores más grandes pueden ejecutarse 100 instancias del mismo script, incluso si solo están recostackndo datos cada 20 minutos cada uno. Esto causa esgulps en la memoria RAM, los límites de conexión de la base de datos, etc. Queremos cambiar de 100 procesos con 1 subproceso a un proceso con 100 subprocesos, cada uno ejecutando el trabajo que, anteriormente, un script estaba realizando.

Pero no es posible cambiar la manera en que la herramienta de monitoreo invoca los scripts. Necesitamos mantener la invocación igual (iniciar un proceso con diferentes parámetros de línea de comandos) pero cambiar los scripts para reconocer que otro está activo, y hacer que el “nuevo” script envíe sus instrucciones de trabajo (desde los parámetros de la línea de comandos) a través de al script “antiguo”.

Por cierto, esto no es algo que quiera hacer en base a un solo script. En su lugar, quiero empaquetar este comportamiento en una biblioteca que muchos autores de secuencias de comandos pueden aprovechar: mi objective es permitir que los autores de secuencias de comandos escriban secuencias de comandos simples y de un solo subproceso que no son conscientes de los problemas de múltiples instancias, y manejar la multihebra. y un solo instancing bajo las mantas.

El enfoque de Alex Martelli para establecer un canal de comunicaciones es el adecuado. Yo usaría un multiprocessing.connection.Listener para crear un oyente, en su elección. Documentación en: http://docs.python.org/library/multiprocessing.html#multiprocessing-listeners-clients

En lugar de usar AF_INET (sockets), puede elegir usar AF_UNIX para Linux y AF_PIPE para Windows. Esperemos que un pequeño “si” no duela.

Edición : Supongo que un ejemplo no haría daño. Aunque es una básica.

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

Esto funciona en OS X, por lo que necesita pruebas con Linux y (después de sustituir la dirección correcta) Windows. Existen muchas advertencias desde el punto de seguridad, la principal es que conn.recv deshace sus datos, por lo que casi siempre es mejor con recv_bytes.

El enfoque general es hacer que el script, al inicio, configure un canal de comunicación de manera que se garantice que sea exclusivo (otros bashs de configurar el mismo canal fallan de manera predecible) para que más instancias del script puedan detectar el El primero está corriendo y habla con él.

Sus requisitos para la funcionalidad multiplataforma apuntan fuertemente hacia el uso de un zócalo como el canal de comunicación en cuestión: puede designar un “puerto conocido” que está reservado para su script, por ejemplo 12345, y abrir un zócalo en ese puerto escuchando solo a localhost ( 127.0.0.1). Si el bash de abrir ese socket falla, porque el puerto en cuestión se “toma”, entonces puede conectarse a ese número de puerto y eso le permitirá comunicarse con el script existente.

Si no está familiarizado con la progtwigción de socket, aquí hay un buen documento de HOWTO . También puede ver el capítulo relevante en Python en una shell de nuez (estoy predispuesto sobre eso, por supuesto ;-).

Tal vez intente usar sockets para la comunicación?

Parece que su mejor apuesta es quedarse con un archivo pid pero no solo que contenga el Id. De proceso, sino que también incluya el número de puerto en el que está escuchando la instancia anterior. Entonces, al iniciar, verifique el archivo pid y, si está presente, verifique si se está ejecutando un proceso con ese Id. Si es así, envíe sus datos y salga, de lo contrario sobrescriba el archivo pid con la información del proceso actual.