Vraag kan een pythonscript weten dat een ander exemplaar van hetzelfde script wordt uitgevoerd ... en er dan mee praten?


Ik wil voorkomen dat meerdere exemplaren van hetzelfde langlopende python-opdrachtregelscript tegelijkertijd worden uitgevoerd en ik zou graag zien dat de nieuwe instantie gegevens naar de oorspronkelijke instantie kan verzenden voordat de nieuwe instantie zelfmoord pleegt . Hoe kan ik dit op een platformonafhankelijke manier doen?

In het bijzonder zou ik het volgende gedrag willen inschakelen:

  1. "foo.py"wordt gestart vanaf de opdrachtregel en blijft nog lange tijd actief - dagen of weken totdat de computer opnieuw wordt opgestart of door het bovenliggende proces wordt gedood.
  2. om de paar minuten wordt hetzelfde script opnieuw gestart, maar met verschillende opdrachtregelparameters
  3. wanneer het wordt gestart, moet het script zien of er nog andere instanties worden uitgevoerd.
  4. Als andere exemplaren worden uitgevoerd, moet instantie # 2 de opdrachtregelparameters naar instantie # 1 verzenden en vervolgens instantie # 2 afsluiten.
  5. exemplaar # 1, als het opdrachtregelparameters van een ander script ontvangt, zou een nieuw onderwerp moeten oprollen en (met behulp van de opdrachtregelparameters verzonden in de stap hierboven) beginnen met het uitvoeren van het werk dat exemplaar # 2 zou uitvoeren.

Dus ik ben op zoek naar twee dingen: hoe kan een python-programma weten dat een ander exemplaar van zichzelf wordt uitgevoerd, en hoe kan een python-opdrachtregelprogramma dan communiceren met een ander?

Om dit gecompliceerder te maken, moet hetzelfde script zowel op Windows als op Linux worden uitgevoerd, dus zou de oplossing idealiter alleen de standaard Python-bibliotheek gebruiken en geen OS-specifieke oproepen. Hoewel als ik een codepath van Vensters en een codepath van * nix (en groot if verklaring in mijn code om een ​​van de twee te kiezen), dat is OK als een oplossing met "dezelfde code" niet mogelijk is.

Ik realiseer me dat ik waarschijnlijk een bestandsgebaseerde aanpak zou kunnen uitwerken (voorbeeld 1 kijkt bijvoorbeeld naar een directory voor wijzigingen en elke instantie plaatst een bestand in die map wanneer het werk wil doen) maar ik maak me een beetje zorgen over het opschonen van die bestanden na een niet-gracieuze machinestilstand. Ik zou idealiter een in-memory-oplossing kunnen gebruiken. Maar nogmaals, ik ben flexibel, als een persistente bestandsgebaseerde aanpak de enige manier is om dit te doen, sta ik open voor die optie.

Meer details: Ik probeer dit te doen omdat onze servers een monitoringtool gebruiken die python-scripts ondersteunt voor het verzamelen van monitoringgegevens (bijvoorbeeld resultaten van een databasequery of webservice-aanroep) die de monitoringtool vervolgens indexeert voor later gebruik. Sommige van deze scripts zijn erg duur om op te starten, maar goedkoop om na het opstarten te worden uitgevoerd (bijvoorbeeld om een ​​DB-verbinding te maken versus een query uit te voeren). Dus we hebben ervoor gekozen om ze in een oneindige lus te laten lopen totdat het parent-proces ze doodt.

Dit werkt prima, maar op grotere servers kunnen 100 exemplaren van hetzelfde script worden uitgevoerd, zelfs als ze alleen om de 20 minuten gegevens verzamelen. Dit veroorzaakt ravages met RAM, DB-verbindingslimieten, enz. We willen van 100 processen overschakelen met 1 thread naar een proces met 100 threads, die elk het werk uitvoeren dat eerder met één script werd uitgevoerd.

Maar het wijzigen van de manier waarop de scripts worden opgeroepen door de monitoringtool is niet mogelijk. We moeten de aanroep op dezelfde manier houden (start een proces met verschillende opdrachtregelparameters) maar verander de scripts om te herkennen dat een andere actief is en laat het "nieuwe" script zijn werkinstructies verzenden (van de opdrachtregelparameters) over naar het "oude" script.

Tussen haakjes, dit is niet iets dat ik wil doen op basis van één script. In plaats daarvan wil ik dit gedrag verpakken in een bibliotheek die door veel scriptauteurs kan worden gebruikt. Mijn doel is om scriptauteurs in staat te stellen eenvoudige scripts met één thread te schrijven die zich niet bewust zijn van problemen met meerdere instanties en de multi-threading aan te kunnen. en single-instancing onder de dekens.


11
2018-05-29 16:46


oorsprong


antwoorden:


De aanpak van Alex Martelli voor het opzetten van een communicatiekanaal is de juiste. Ik zou een multiprocessing.connection.Listener gebruiken om een ​​luisteraar te maken, naar keuze. Documentatie op: http://docs.python.org/library/multiprocessing.html#multiprocessing-listeners-clients

In plaats van AF_INET (sockets) te gebruiken, kunt u ervoor kiezen om AF_UNIX voor Linux en AF_PIPE voor Windows te gebruiken. Hopelijk zou een klein "als" geen pijn doen.

Bewerk: Ik denk dat een voorbeeld geen kwaad zou doen. Het is een standaard.

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

Dit werkt op OS X, dus het moet getest worden met zowel Linux als (na het vervangen van het juiste adres) Windows. Er zijn veel kanttekeningen bij een beveiligingspunt, met als belangrijkste dat conn.recv zijn gegevens ontgrendelt, zodat je bijna altijd beter bent met recv_bytes.


10
2018-05-29 18:16



De algemene benadering is om het script bij het opstarten een communicatiekanaal in te stellen op een manier die gegarandeerd exclusief is (andere pogingen om hetzelfde kanaaldefect op een voorspelbare manier in te stellen), zodat verdere exemplaren van het script de de eerste is aan het rennen en praat ermee.

Uw vereisten voor platformonafhankelijke functionaliteit wijzen sterk op het gebruik van een socket als het communicatiekanaal in kwestie: u kunt een "bekende poort" aanduiden die is gereserveerd voor uw script, bijvoorbeeld 12345, en een socket op die poort openen die alleen naar localhost luistert ( 127.0.0.1). Als de poging om die socket te openen mislukt, omdat de betreffende poort is "genomen", dan kunt u in plaats daarvan verbinding maken met dat poortnummer, en dat laat u communiceren met het bestaande script.

Als u niet bekend bent met socketprogrammering, is er een goede zaak HOWTO dokter hier. Je kunt ook kijken naar het betreffende hoofdstuk in Python in een notendop (Ik heb daar natuurlijk vooringenomen ;-).


9
2018-05-29 16:54



Misschien probeert u sockets te gebruiken voor communicatie?


1
2018-05-29 16:50



Klinkt alsof je het beste kunt vasthouden aan een pid-bestand, maar deze niet alleen de proces-ID bevat, maar ook het poortnummer waarop het vorige exemplaar luistert. Controleer dus bij het opstarten naar het pid-bestand en kijk, indien aanwezig, of een proces met die ID loopt - stuur dan uw gegevens ernaar en sluit anders overschrijf het pid-bestand met de informatie van het huidige proces.


0
2018-05-29 18:12