web-dev-qa-db-ja.com

並列I / Oバインドタスクのスケジュール(バックアップソリューション)

Pythonにバックアップソリューションを実装したいのですが、バックアップサーバーが複数の仮想サーバーと物理サーバーでバックアップを開始します(1サーバー= 1バックアップタスク)。実際のバックアップタスクの詳細は無視します。今のところ、スケジューリング/マルチプロセッシングの部分に関心があります。

私が持っている制約は:

  1. 一度に2つのサーバーのみをバックアップします(たとえば、最大2つのバックアップスレッドを同時に実行します)。
  2. 同じ物理マシン上の2つのサーバーをバックアップしないでください(多くの場合、複数の仮想サーバーが共通のハードウェアマシンを共有します)。

Pythonでのマルチプロセッシングの経験があまりないので、最適なPythonソリューションが何であるかを考えています。以下が私の頭に浮かびました:

  • 各バックアップジョブ(各サーバーなど)ごとにスレッドを作成し、threading.BoundedSemaphoreを使用すると、2つだけが同時に実行されます。より多くのセマフォ/条件を使用して、複数のスレッドが同じ物理マシン上の2つのサーバーをバックアップしないようにします。
  • 常に実行されている2つのスレッドがあり、そのタスクをキューから取得します。同時に、キューは、同じ物理マシン上のタスクが一度に渡されないことを確認する必要があります(たとえば、タスクのスキップ/並べ替え)。私はおそらくこれをQueue.PriorityQueue追加の制約を追加します。

私は2番目のオプションに傾いていますが、キューが複数の作業スレッドにタスクを渡すのに適切なデータ構造であるかどうかはわかりません。実行時にキューにタスクを追加する必要はありません(キューで許可されています)。線形順序でタスクを処理するだけでなく、タスクを渡すための少しのロジックが必要です。これにはより良い(標準の)データ構造がありますか?

より経験豊富なプログラマからの意見を聞いていただければありがたいです。

2
Simon Fromme

物理マシンごとにキューがあります。

したがって、バックアッププロセスは最大でN = 2のタスクを実行し、別のタスクがすでに実行されているキューからタスクを選択しないようにします。

(Nは必要に応じて簡単に調整できます。)

2
9000

人生をはるかに簡単にし、定型コードをはるかに少なくするために、 Advanced Python Scheduler を確認することをお勧めします

作業を開始するのは非常に簡単で、キュー、スレッド、およびスケジューリングの管理の時間と手間をすべて省きます。

マルチプロセスまたはスレッドモードで実行できます。簡単な設定で一度に実行するタスクの数を制限できます。 ここの例を参照

それはさらに良くなり、すでに前のタスクがまだ実行されている場合は同じタスクを再度実行せず、max_instances: 1を設定して終了するまで待機します。
状態が必要な場合は、ジョブストアを使用してタスクを永続化できます。

コード例:

from pytz import utc

from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.mongodb import MongoDBJobStore
from apscheduler.jobstores.redis import RedisJobStore
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor


jobstores = {
    'mongo': MongoDBJobStore(),
    'redis' : RedisJobStore(),
    'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite')
}
executors = {
    'default': ThreadPoolExecutor(20),
    'processpool': ProcessPoolExecutor(5)
}
job_defaults = {
    'coalesce': False,
    'max_instances': 3
}
scheduler = BackgroundScheduler(jobstores=jobstores, executors=executors, job_defaults=job_defaults, timezone=utc)  

def backup_job(**kwargs):
    print('doing backup stuff')


scheduler.add_job(backup_job, 'interval', seconds=600, jobstore='redis'
                  id=1, name='backup', kwargs={'key':'value'})

それが役に立てば幸い

1
Urban48