14

同じ長時間実行のPythonコマンドラインスクリプトの複数のインスタンスが同時に実行されないようにしたい。また、新しいインスタンスが自殺する前に、新しいインスタンスが元のインスタンスにデータを送信できるようにしたい。 。クロスプラットフォームの方法でこれを行うにはどうすればよいですか?

具体的には、次の動作を有効にします。

  1. " foo.py"はコマンドラインから起動され、マシンが再起動されるか、親プロセスがマシンを強制終了するまで、数日または数週間、長時間実行され続けます。
  2. 数分ごとに同じスクリプトが再度起動されますが、コマンドラインパラメータが異なります
  3. 起動すると、スクリプトは他のインスタンスが実行されているかどうかを確認する必要があります。
  4. 他のインスタンスが実行されている場合、インスタンス#2はコマンドラインパラメーターをインスタンス#1に送信してから、インスタンス#2を終了する必要があります。
  5. インスタンス#1は、別のスクリプトからコマンドラインパラメータを受け取った場合、新しいスレッドを起動し、(上記の手順で送信されたコマンドラインパラメータを使用して)インスタンス#2が実行しようとしていた作業の実行を開始する必要があります。

だから私は2つのことを探しています:Pythonプログラムがそれ自体の別のインスタンスが実行されていることをどのように知ることができますか、そして1つのPythonコマンドラインプログラムが別のプログラムとどのように通信できますか?

これをさらに複雑にするには、同じスクリプトをWindowsとLinuxの両方で実行する必要があるため、理想的には、ソリューションはPython標準ライブラリのみを使用し、OS固有の呼び出しは使用しません。Windowsコードパスと*nixコードパス(およびifどちらかを選択するためのコード内の大きなステートメント)が必要な場合でも、「同じコード」ソリューションが不可能な場合は問題ありません。

私はおそらくファイルベースのアプローチを実行できることを理解しています(たとえば、インスタンス#1はディレクトリの変更を監視し、各インスタンスは作業を行いたいときにそのディレクトリにファイルをドロップします)が、それらのファイルのクリーンアップについて少し心配しています正常でないマシンのシャットダウン後。理想的には、メモリ内ソリューションを使用できると思います。しかし、繰り返しになりますが、永続ファイルベースのアプローチがそれを行う唯一の方法である場合、私はそのオプションを受け入れます。

詳細:サーバーがPythonスクリプトの実行をサポートする監視ツールを使用して監視データ(データベースクエリやWebサービス呼び出しの結果など)を収集し、後で使用するためにインデックスを作成しているため、これを実行しようとしています。これらのスクリプトの中には、起動に非常に費用がかかるものもありますが、起動後に実行するのは安価です(たとえば、DB接続の作成とクエリの実行)。そのため、親プロセスがそれらを強制終了するまで、それらを無限ループで実行し続けることを選択しました。

これはうまく機能しますが、大規模なサーバーでは、それぞれ20分ごとにデータを収集している場合でも、同じスクリプトの100個のインスタンスが実行されている可能性があります。これは、RAM、DB接続制限などに大混乱をもたらします。1スレッドの100プロセスから100スレッドの1プロセスに切り替えて、それぞれが以前は1つのスクリプトで実行していた作業を実行します。

ただし、監視ツールによるスクリプトの呼び出し方法を変更することはできません。呼び出しを同じに保つ必要があります(異なるコマンドラインパラメーターでプロセスを起動します)が、別のスクリプトがアクティブであることを認識するようにスクリプトを変更し、「新しい」スクリプトに(コマンドラインパラメーターからの)作業指示を送信させます「古い」スクリプトに。

ところで、これは私が1スクリプトベースでやりたいことではありません。代わりに、この動作を多くのスクリプト作成者が利用できるライブラリにパッケージ化したいと思います。私の目標は、スクリプト作成者がマルチインスタンスの問題を認識しない単純なシングルスレッドスクリプトを記述し、マルチスレッドを処理できるようにすることです。カバーの下でシングルインスタンス化。

4

4 に答える 4

11

通信チャネルを設定するAlexMartelliのアプローチが適切です。multiprocessing.connection.Listenerを使用して、選択したリスナーを作成します。ドキュメント:http: //docs.python.org/library/multiprocessing.html#multiprocessing-listeners-clients

AF_INET(ソケット)を使用する代わりに、Linuxの場合はAF_UNIXを使用し、Windowsの場合はAF_PIPEを使用することを選択できます。うまくいけば、小さな「if」が傷つかないでしょう。

編集:私は例が害を及ぼすことはないと思います。ただし、これは基本的なものです。

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

これはOSXで機能するため、Linuxと(正しいアドレスに置き換えた後の)Windowsの両方でテストする必要があります。セキュリティの観点からは多くの注意点があります。主な注意点は、conn.recvがデータ​​の選択を解除することです。そのため、ほとんどの場合、recv_bytesの方が適しています。

于 2010-05-29T18:16:10.877 に答える
9

一般的なアプローチは、スクリプトの起動時に、排他的であることが保証されている方法で通信チャネルを設定することです(同じチャネルを設定する他の試みは予測可能な方法で失敗します)。これにより、スクリプトのさらなるインスタンスが最初に走っ話をします。

クロスプラットフォーム機能の要件は、問題の通信チャネルとしてソケットを使用することを強く示しています。スクリプト用に予約されている「既知のポート」、たとえば12345を指定し、ローカルホストのみをリッスンしているそのポートでソケットを開くことができます( 127.0.0.1)。問題のポートが「取得」されたためにそのソケットを開こうとして失敗した場合は、代わりにそのポート番号に接続できます。これにより、既存のスクリプトと通信できるようになります。

ソケットプログラミングに慣れていない場合は、ここHOWTOに優れたドキュメントがあります。Pythonの関連する章を一言で見ることもできます(もちろん、私はその章に偏っています;-)。

于 2010-05-29T16:54:09.020 に答える
1

おそらく、通信にソケットを使用してみてください。

于 2010-05-29T16:50:42.320 に答える
0

最善の策はpidファイルに固執しているように聞こえますが、プロセスIDだけでなく、前のインスタンスがリッスンしているポート番号も含まれている必要があります。したがって、起動時にpidファイルを確認し、存在する場合はそのIDのプロセスが実行されているかどうかを確認します。実行されている場合はデータを送信し、終了する場合はpidファイルを現在のプロセスの情報で上書きします。

于 2010-05-29T18:12:51.033 に答える