web-dev-qa-db-ja.com

ギアマンの労働者をうまく止める

多くのGearmanワーカーが常に実行されており、ユーザーページビューの記録などを保存しています。Gearmanワーカーが使用するPHPコードを更新することがあります。ワーカーに新しいコードに切り替えてもらいます。ワーカーのPHPプロセスを強制終了して再起動します。

これを行うためのより良い方法は何ですか?おそらく、これらのワーカープロセスの1つを強制終了すると、データが失われることがあります(それほど重要なデータではありませんが)。

編集:私は私のために働く答えを見つけて、それを以下に投稿しました。

43
Karptonite

さて、私はこの質問を投稿しました、今私はそれに良い答えを見つけたと思います。

Net_Gearman_Workerのコードを見ると、作業ループで関数stopWorkが監視されており、trueが返されると、関数が終了していることがわかります。

私は次のことをしました:
memcacheを使用して、キャッシュされた値gearman_restarttimeを作成し、別のスクリプトを使用して、サイトを更新するたびにそれを現在のタイムスタンプに設定します。 (私はMemcacheを使用しましたが、これはデータベース、ファイルなど、どこにでも保存できます)。

私はWorkerクラスを基本的にNet_Gearman_Worker_Fooに拡張し、すべてのワーカーにそれをインスタンス化させました。 Fooクラスでは、stopWork関数をオーバーライドして、次のことを行います。まず、gearman_restarttimeをチェックします。初回は、値をグローバル変数に保存します。それ以降、毎回、キャッシュされた値をグローバルと比較します。変更された場合、stopWorkはtrueを返し、ワーカーは終了します。 cronは毎分チェックして、各ワーカーがまだ実行されているかどうかを確認し、終了したワーカーを再起動します。

StopWorkにもタイマーを設定し、x分ごとに1回だけキャッシュをチェックすることをお勧めします。私たちの場合、Memcacheは十分に高速なので、毎回値をチェックすることは問題ではないようですが、他のシステムを使用して現在のタイムスタンプを保存している場合は、チェックの頻度を減らす方がよいでしょう。

7
Karptonite

解決策1


通常、私はワーカーを-rフラグ付きのunixデーモンユーティリティで実行し、1つのジョブの後に期限切れにします。スクリプトは各反復後に正常に終了し、デーモンは自動的に再起動します。

あなたの労働者は1つの仕事のために古くなりますが、それはデータを失うことほどあなたにとって大したことではないかもしれません

このソリューションには、メモリを解放するという利点もあります。 PHP 5.3より前のバージョンにはひどいGCがあるため、大規模なジョブを実行している場合は、メモリに問題が発生する可能性があります。

解決策2


スクリプトを終了するすべてのワーカーに終了関数を追加することもできます。再起動したいときは、ギアマンに高い優先度で終了するように電話をかけるだけです。

12
bnmrrs
function AutoRestart() {
   static $startTime = time();

   if (filemtime(__FILE__) > $startTime) {
      exit();
   }
}

AutoRestart();  
8
Orwellophile

うーん、あなたは労働者にコードを実装して、ソースコードが変更されたかどうかを時々チェックすることができます。つまり、ジョブの途中で、ジョブが非常に大きいかどうかを確認します。

他の方法は、ある種の割り込みを実装することです。おそらくネットワークを介して、機会があればいつでも停止して再起動します。

最後の解決策は、Gearmanのソースを変更してこの機能を含めることです。

1
Yarek T

誰かがPerlを実行しているワーカーの答えを探していた場合、それは GearmanX :: Starter ライブラリの目的の一部です。現在のジョブを完了した後、2つの異なる方法でワーカーを停止できます。外部でワーカープロセスにSIGTERMを送信するか、プログラムでグローバル変数を設定します。

1
runrig

私は最近これも調べています(Gearman :: XSを使用したPerlでは)。私のユースケースはあなたのユースケースと同じでした。長期にわたるギアマンワーカーが定期的に新しいバージョンをチェックしてリロードできるようにしてください。

私の最初の試みは、ワーカースクリプトのバージョンを最後にチェックしてからの時間をワーカーに追跡させることでした(md5sumも機能します)。次に、N秒が経過すると、ジョブ間で、それ自体の新しいバージョンが使用可能かどうかを確認し、それ自体を再起動します(fork()/ exec())。これは問題なく機能しましたが、まれなジョブに登録されたワーカーは、work()が戻るまで、つまり現在の時刻を確認するために何時間も待機する可能性があります。

そのため、work()でジョブを待機するときにかなり短いタイムアウトを設定しているので、時間をより定期的に確認できます。 PHPインターフェースは、ジョブの登録時にこのタイムアウト値を設定できることを示しています。私はSIGALRMを使用して新しいバージョンのチェックをトリガーしています。Perlインターフェースはwork()でブロックするため、アラームは最初はトリガーされていませんでした。タイムアウトを60秒に設定すると、SIGALRMが機能しました。

1
d5ve

ワーカーはPHPで記述されているため、既知のスケジュールでリサイクルすることをお勧めします。これは、開始してから静的な時間である場合もあれば、特定の数のジョブが試行された後に実行される場合もあります。

これは本質的に1つの石で2羽の鳥を殺します(しゃれは意図されていません)。メモリリークの可能性を軽減しており、ワーカーが新しい可能性のあるコードをいつ取得するかを一貫して判断する方法があります。

私は通常、労働者がプロセスのどこにいるかを簡単に確認できるように、間隔をstdoutやログ機能に報告するように労働者を作成します。

1
Wil Moore III

http://phpscaling.com/2009/06/23/doing-the-work-elsewhere-sidebar-running-the-worker/

上記の記事が示すように、私はBASHシェルスクリプト内でワーカーを実行し、クリーンアップ(またはワーカースクリプトの再読み込み)のためにジョブの間に時々終了します-または、特定のタスクが与えられた場合、特定のタスクで終了できます終了コードとシャットダウンします。

1
Alister Bulman

私はこれと同じ問題に遭遇し、python 2.7の解決策を思いつきました。

ギアマンを使用してシステム上の他のコンポーネントと通信するpythonスクリプトを作成しています。スクリプトには複数のワーカーがあり、各ワーカーは別々のスレッドで実行されます。ワーカーはすべてギアマンを受け取ります。データを処理してメッセージキューに保存し、メインスレッドは必要に応じてデータをキューから引き出すことができます。

各ワーカーをクリーンにシャットダウンするための私の解決策は、_gearman.GearmanWorker_をサブクラス化し、work()関数をオーバーライドすることでした。

_from gearman import GearmanWorker
POLL_TIMEOUT_IN_SECONDS = 60.0
class StoppableWorker(GearmanWorker):
    def __init__(self, Host_list=None):
        super(StoppableWorker,self).__init__(Host_list=Host_list)
        self._exit_runloop = False


    # OVERRIDDEN
    def work(self, poll_timeout=POLL_TIMEOUT_IN_SECONDS):
        worker_connections = []
        continue_working = True

        def continue_while_connections_alive(any_activity):
            return self.after_poll(any_activity)

        while continue_working and not self._exit_runloop:
            worker_connections = self.establish_worker_connections()
            continue_working = self.poll_connections_until_stopped(
                worker_connections,
                continue_while_connections_alive,
                timeout=poll_timeout)

        for current_connection in worker_connections:
            current_connection.close()

        self.shutdown()


    def stopwork(self):
        self._exit_runloop = True
_

GearmanWorkerと同じように使用してください。スクリプトを終了するときは、stopwork()関数を呼び出します。すぐには停止しません。実行ループから開始するまでに最大_poll_timeout_秒かかる場合があります。

stopwork()関数を呼び出すための賢い方法は複数あるかもしれません。私の場合、メインスレッドに一時的なギアマンクライアントを作成します。シャットダウンしようとしているワーカーに対して、ギアマンサーバーを介して特別なSTOPコマンドを送信します。ワーカーがこのメッセージを受け取ると、ワーカーは自分自身をシャットダウンすることを認識しています。

お役に立てれば!

1
RobotNerd

これは、継続的インテグレーションシステムにうまく適合します。私はあなたがそれを持っているか、あなたがすぐにそれを持っているべきであることを願っています:-)

新しいコードをチェックインすると、コードは自動的にビルドされ、サーバーにデプロイされます。ビルドスクリプトの一部として、すべてのワーカーを強制終了し、新しいワーカーを起動します。

0
Alex Weinstein

私がしていることは、gearmadminを使用して、実行中のジョブがあるかどうかを確認することです。このためのUIを作成するために管理APIを使用しました。仕事がぼんやりと座っているとき、彼らを殺しても害はありません。

0
kagronick

_Ctrl-C_と_kill -TERM_の両方をサポートする次のコードを使用します。デフォルトでは、_signal=_設定を変更していない場合、supervisorTERMシグナルを送信します。 PHP 5.3+ declare(ticks = 1)は非推奨です。代わりに、pcntl_signal_dispatch()を使用してください。

_$terminate = false;
pcntl_signal(SIGINT, function() use (&$terminate)
{
    $terminate = true;
});
pcntl_signal(SIGTERM, function() use (&$terminate)
{
    $terminate = true;
});

$worker = new GearmanWorker();
$worker->addOptions(GEARMAN_WORKER_NON_BLOCKING);
$worker->setTimeout(1000);
$worker->addServer('127.0.0.1', 4730);
$worker->addFunction('reverse', function(GearmanJob $job)
{
    return strrev($job->workload());
});

$count = 500 + Rand(0, 100); // Rand to prevent multple workers restart at same time
for($i = 0; $i < $count; $i++)
{
    if ( $terminate )
    {
        break;
    }
    else
    {
        pcntl_signal_dispatch();
    }

    $worker->work();

    if ( $terminate )
    {
        break;
    }
    else
    {
        pcntl_signal_dispatch();
    }

    if ( GEARMAN_SUCCESS == $worker->returnCode() )
    {
        continue;
    }

    if ( GEARMAN_IO_WAIT != $worker->returnCode() && GEARMAN_NO_JOBS != $worker->returnCode() )
    {
        $e = new ErrorException($worker->error(), $worker->returnCode());
        // log exception
        break;
    }

    $worker->wait();
}

$worker->unregisterAll();
_
0
happy_marmoset