2012-07-16 17 views
7

私はこれについてもっと見つけられなかったことに驚いていますが、悲しいかな、私はまだ答えを見つけることができません。私たちは最近、AWSに変換し、シンプルなウェブサイトをより堅牢で信頼性の高いシステムに移行しました。現在、私が困惑しているのは、そのcronジョブが環境内のすべてのインスタンスにプッシュされるときに、分散システム上でcronジョブを管理することです。ここでAWSのCron(または一般的な分散システム)

は、ユースケースです:

背景

セットアップ

私たちは、伝統的なLAMPスタックを実行しています。おそらく最初の問題ですが、それは私たちが得たものです。

DBテーブル

table1 

- id int(11) 
- start date 
- interval int(11) (number of seconds) 

table2 

- id int(11) 
- table1_id int(11) 
- sent datetime 

ゴール

目標は、スクリプトは毎日一回実行すると、以下のチェックすることです:

  1. 現在の日付が過去であるがtable1.start
  2. table1.start <現在の日付
  3. table1.interval> 0
  4. 今日は離れて正確に全区間である(その間隔は[秒で] 7日だったし、それが6日目である場合に失敗します)
  5. table2.sentが今日あるようなtable2にエントリがありません前のチェックと一致するのはtable2.table1_idです。

これらのチェックがすべて合格した場合、間隔を持つテーブル1ごとにtable2にエントリを挿入します。これはまた、表2のデータに基づいて電子メールを送信することを意味します。

問題基本的に

、我々は前述のブロックによって表される2つのクエリを、持っています。問題は、分散システムでは、各インスタンスが同時に(または互いにミリ秒以内に)cronを実行するということです。 「トランザクション」という概念はないので、他の人が最初のクエリを実行する前にtable2に挿入する機会がないと、各インスタンスは電子メールを送信します。

解決策???

私はこの研究のかなりの量を行っているが、私は出ている唯一の潜在的な解決策を以下に詳述する

cronのインスタンス

がランニングを担当し、単一の、独立したインスタンスを設定しますcronジョブ。これは確かに(私が見る限り)仕事ですが、ひどく高価ではなく、たいてい1日に1回だけ実行する必要がある仕事には、非常にコストがかかります。

PHPスケジューラ

セットcronが定期的にスケジューラとして動作するPHPスクリプトを実行します。これは、我々の研究が限られた時間とお金のために最も簡単であると示唆した後に我々が下降していたルートであった。私が遭遇した問題は、並行処理の問題をジョブの消費からジョブのスケジューリングに変えるように思えたことでした。 cronを実行している各インスタンスから複数のジョブが同時にスケジュールされないようにジョブをスケジュールするのはいつですか?

この方法も(私の友人の好きな言葉を借りるために)非常に "クルージュ"と思われ、私は同意する必要があります。

取引

私はこのかなり研究してきたが

は、並行処理は常にデータベース上のアトミックトランザクションを解決した、これまでのところ、私は言うことができるように、これはLAMPを達成することは容易ではありません。しかし、おそらく私は間違っています。私はそう証明されてとても幸せです。

最後に

誰も私はこの1つを把握することができますのであれば、私はそれを大幅に感謝。おそらく、私のグーグルのスキルは錆びていますが、私はこの(おそらく単純な)仕事に苦しんでいる唯一の人だとは想像できません。

+1

これを実際に建設的な答えに変えるのに十分な経験はありませんが、AmazonのSWFを見てきましたか?すでにAWSに乗っているので、cronの代わりに信頼できるものです。 –

+0

恐ろしいと思われるかもしれませんが、[Zookeeper](http://zookeeper.apache.org/)を見てください。軽量で堅牢な使い方が簡単で、分散したタスクをできるだけシンプルに調整/同期化する作業を行います。 – Viccari

+0

Kohanaを使用していることも注目に値するかもしれません。トランザクションがアトミックで直列になっていることを確認するために、DBクエリで行うことができるロックのレベルがあるかどうか疑問に思っています。 – Ryan

答えて

3

ギアマンプロジェクトhttp://www.gearman.orgをご覧ください。基本的なアーキテクチャーでは、ジョブ・サーバーである1台のマシンと、すべての他のマシンがサーバーのクライアントになるということです。

ジョブ・サーバーでcrontabを設定して、Gearmanを介して接続されているすべてのクライアントに実行するコマンドを送信できます。その後、PHPを使用してcronジョブをスライスして切り抜き、必要に応じてMap/Reduceを深く理解することができます。

ここでは概念上の良いチュートリアルだとそれがどのように働くか:

http://www.lornajane.net/posts/2011/Using-Gearman-from-PHPはすぐにGearmanのようなものでの作業については落胆取得しないでください。分散cronシステムは複雑なこともありますが、一度それを周りに頭を上げると大丈夫です。

FWIWでは、Amazon EC2のGearmanワーカーファームで毎分数千のcronスクリプトを処理しています。私たちは絶対にそれを愛しています。

4

私は同様の問題がありました。そして私は毎分実行する必要のあるcronジョブを持っていましたが、単一のホストでのみ

私はこのハックを使って解決しました。このツールは、実行中のボックスが最後のものかどうかを調べるためにこの自動スケーリンググループでインスタンス化されます。これは明らかに自動拡張を使用し、ホスト名にインスタンスIDが含まれていることを前提としています。

#!/usr/bin/env ruby 

AWS_AUTO_SCALING_HOME='/opt/AutoScaling' 
AWS_AUTO_SCALING_URL='https://autoscaling.eu-west-1.amazonaws.com' 
MY_GROUP = 'Production' 

@cmd_out = `bash -c 'AWS_AUTO_SCALING_HOME=#{ AWS_AUTO_SCALING_HOME }\ 
    AWS_AUTO_SCALING_URL=#{ AWS_AUTO_SCALING_URL }\ 
    #{ AWS_AUTO_SCALING_HOME }/bin/as-describe-auto-scaling-instances'` 

raise "Output empty, should not happen!" if @cmd_out.empty? 
@lines = @cmd_out.split(/\r?\n/) 
@last = @lines.select {|l| l.match MY_GROUP }.reverse. 
    detect { |l| l =~ /^INSTANCE\s+\S+\s+\S+\s+\S+\s+InService\s+HEALTHY/ } 
raise "No suitable host in autoscaling group!" unless @last 
@last_host = @last.match(/^INSTANCE\s+(\S+)/)[1] 
@hostname = `hostname` 
if @hostname.index(@last_host) 
    puts "It's me!" 
    exit(0) 
else 
    puts "Someone else will do it!" 
    exit(1) 
end 

は、それを保存としては/ usr/binに/ lastonly、その後、cronジョブで私が:

lastonly && do_my_stuff 

を明らかにそれは完璧ではないのですが、それは私のために動作し、それは簡単です!

関連する問題