記事の著者およびジャーナルPragmaticPerl.comの編集長の許可を得て。 元の記事はここにあります。
スターマン?
このサーバーの作成者(宮川達彦)は、彼について次のように述べています。
「スターマンの名前は、日本のロックバンド、ユニコーン(はい、ユニコーン)によるスターHA男の歌に由来しています。 デヴィッド・ボウイには、同じ名前の歌「スターマン」があります。これは、カルト日本のゲーム「アースバウンド」のキャラクターの名前、スーパーマリオブラザーズの音楽テーマの名前です。
HTTPのようなPerlモジュールの命名にうんざりしている::サーバー:: PSGI ::方法::その::書かれたもの::と::何::モジュール、そしてその結果、人々はそれをIRCでHSPHIWWWMと呼ぶ。 これはあまり発音されず、初心者には問題が生じます。 はい、多分私は見落としです。 時間が経てます。」
名前が整理されています。 次に、サーバー自体を扱います。
プリフォーク?
スターマンのプリフォークモデルは、最も強力なUnixサーバーに似ています。 事前実行プロセスのモデルを使用します。 また、ワーカーのプールを自動的に再起動し、ゾンビプロセスを削除します。
Plackアプリケーション
今回は、Plackアプリケーションは完全に基本的なものになります。
use strict; use warnings; use Plack; use Plack::Builder; use Plack::Request; sub body { return 'body'; } sub body2 { return shift; } my $app = sub { my $env = shift; my $req = Plack::Request->new($env); my $res = $req->new_response(200); $res->body(body()); return $res->finalize(); }; my $main_app = builder { mount "/" => builder { $app }; };
Starmanで開発する場合、その作業の非常に重要なポイントを理解する必要があります。 たとえば、データベース接続を検討してください。 多くの場合、時間とコードの行を節約するために、接続の初期化はスクリプトの最初に行われます。 これはCGIに適用され、FastCGIにも適用されます。 これはPSGIには当てはまりません。 そして、ここに理由があります。 サーバーが起動すると、このコードはワーカーごとに1回だけ実行されます。 また、この状況の危険性は、最初は、タイムアウトによって接続がクラッシュするか、何らかの理由でアプリケーションが通常どおりに動作することです。 非同期サーバーの場合、アプリケーションコードの最初で接続プールを初期化できます(connection!=接続プール)。
これを確認または反論するために、アプリケーションコードを変更します。 インポート後、コードの先頭に次の行を追加します。
warn 'AFTER IMPORT';
これで、アプリケーションは次のようになります。
use strict; use warnings; use Plack; use Plack::Builder; use Plack::Request; warn 'AFTER IMPORT'; sub body { return 'body'; } sub body2 { return shift; } my $app = sub { my $env = shift; my $req = Plack::Request->new($env); my $res = $req->new_response(200); $res->body(body()); return $res->finalize(); }; my $main_app = builder { mount "/" => builder { $app }; };
実験の純度を高めるために、次のコマンドを使用して1人のワーカーでstarmanを実行します。
starman --port 8080 --workers 1 app.psgi
app.psgiはアプリケーションです。
起動が完了するとすぐに、STDERRに次の画像が表示されます。
noxx@noxx-inferno ~/perl/psgi $ starman --port 8080 app.psgi --workers 1 2013/06/02-15:05:31 Starman::Server (type Net::Server::PreFork) starting! pid(4204) Resolved [*]:8080 to [::]:8080, IPv6 Not including resolved host [0.0.0.0] IPv4 because it will be handled by [::] IPv6 Binding to TCP port 8080 on host :: with IPv6 Setting gid to "1000 1000 4 24 27 30 46 107 125 1000 1001" AFTER IMPORT at /home/noxx/perl/psgi/app.psgi line 7.
localhost:8080 /にリクエストを送信すると、STDERRに新しいものが何も表示されず、サーバーが正常に応答していることを確認できます。
ワーカーが実際に1であることを確認するには、次のコマンドを実行します。
ps uax | grep starman
結果:
noxx 4204 0.6 0.1 57836 11264 pts/3 S+ 15:05 0:00 starman master --port 8080 app.psgi --workers 1 noxx 4205 0.2 0.1 64708 13164 pts/3 S+ 15:05 0:00 starman worker --port 8080 app.psgi --workers 1 noxx 4213 0.0 0.0 13580 940 pts/4 S+ 15:05 0:00 grep --colour=auto starman
2つのプロセスがあります。 しかし、実際には労働者はそのうちの1人にすぎません。 別の実験をしてみましょう。 3人の労働者でスターマンを実行します。
starman --port 8080 --workers 3 app.psgi
結果:
2013/06/02-15:11:08 Starman::Server (type Net::Server::PreFork) starting! pid(4219) Resolved [*]:8080 to [::]:8080, IPv6 Not including resolved host [0.0.0.0] IPv4 because it will be handled by [::] IPv6 Binding to TCP port 8080 on host :: with IPv6 Setting gid to "1000 1000 4 24 27 30 46 107 125 1000 1001" AFTER IMPORT at /home/noxx/perl/psgi/app.psgi line 7. AFTER IMPORT at /home/noxx/perl/psgi/app.psgi line 7. AFTER IMPORT at /home/noxx/perl/psgi/app.psgi line 7.
わかった。 次に、プロセスのリストを見てみましょう。 私にとってはこのように見えます:
noxx 4219 0.1 0.1 57836 11264 pts/3 S+ 15:11 0:00 starman master --port 8080 app.psgi --workers 3 noxx 4220 0.0 0.1 64460 12756 pts/3 S+ 15:11 0:00 starman worker --port 8080 app.psgi --workers 3 noxx 4221 0.0 0.1 64460 12920 pts/3 S+ 15:11 0:00 starman worker --port 8080 app.psgi --workers 3 noxx 4222 0.0 0.1 64460 12756 pts/3 S+ 15:11 0:00 starman worker --port 8080 app.psgi --workers 3 noxx 4224 0.0 0.0 13580 936 pts/4 S+ 15:12 0:00 grep --colour=auto starman
マスター1人、ワーカー3人。
実行順序を把握しました。 次に、別の警告を追加します。
warn 'IN BUILDER'
アプリケーションは次のとおりです。
use strict; use warnings; use Plack; use Plack::Builder; use Plack::Request; warn 'AFTER IMPORT'; sub body { return 'body'; } sub body2 { return shift; } my $app = sub { my $env = shift; my $req = Plack::Request->new($env); my $res = $req->new_response(200); $res->body(body()); return $res->finalize(); }; my $main_app = builder { warn 'IN BUILDER'; mount "/" => builder { $app }; };
1つのワーカープロセスの場合、出力は次のようになります(開始コマンド:starman --port 8080 --workers 1 app.psgi):
2013/06/02-17:33:27 Starman::Server (type Net::Server::PreFork) starting! pid(4430) Resolved [*]:8080 to [::]:8080, IPv6 Not including resolved host [0.0.0.0] IPv4 because it will be handled by [::] IPv6 Binding to TCP port 8080 on host :: with IPv6 Setting gid to "1000 1000 4 24 27 30 46 107 125 1000 1001" AFTER IMPORT at /home/noxx/perl/psgi/app.psgi line 7. IN BUILDER at /home/noxx/perl/psgi/app.psgi line 23.
3つのワーカーでアプリケーションを実行すると、STDERRに次の図が表示されます。
AFTER IMPORT at /home/noxx/perl/psgi/app.psgi line 7. IN BUILDER at /home/noxx/perl/psgi/app.psgi line 23. AFTER IMPORT at /home/noxx/perl/psgi/app.psgi line 7. IN BUILDER at /home/noxx/perl/psgi/app.psgi line 23. AFTER IMPORT at /home/noxx/perl/psgi/app.psgi line 7. IN BUILDER at /home/noxx/perl/psgi/app.psgi line 23.
localhost:8080 /にリクエストを送信することにより、STDERRに新しいものが何も表示されないことを簡単に確認できます。
次の結論を出すことができます。
このアクションは 、アプリケーションの起動時に実行されます。 これは、スクリプトの開始と、存在する場合はビルダーセクションの両方に当てはまります。
このアクションは 、サーバー要求では実行されません 。
Starmanワークフローは順番に開始されます。
これにより、スクリプトの開始時とビルダー部分の両方で重いオブジェクトを構築できます。
そして、次の形式の別の警告をコードに追加しましょう。
warn 'REQUEST';
そして、アプリケーションを次のフォームに持っていきましょう。
use strict; use warnings; use Plack; use Plack::Builder; use Plack::Request; warn 'AFTER IMPORT'; sub body { return 'body'; } sub body2 { return shift; } my $app = sub { warn 'REQUEST'; my $env = shift; my $req = Plack::Request->new($env); my $res = $req->new_response(200); $res->body(body()); return $res->finalize(); }; my $main_app = builder { warn 'IN BUILDER'; mount "/" => builder { $app }; };
1つのワークフローでアプリケーションを実行します(starman --port 8080 --workers 1 app.psgi)。 これまでのところ何も変わっていません。
AFTER IMPORT at /home/noxx/perl/psgi/app.psgi line 7. IN BUILDER at /home/noxx/perl/psgi/app.psgi line 24.
ただし、STDERRに新しいエントリを表示するようリクエストすることは価値があります。
REQUEST at /home/noxx/perl/psgi/app.psgi line 16.
まとめると。 starmanへの各リクエストでは、アプリケーション自体のコードのみが実行されます(return sub ...を覚えておく価値があります)が、起動時にこのコードは実行されません。
そして今、一つのプロセスが落ちたとしましょう。 sub ...を返す次の行を追加します。
die("DIED");
その結果、次の形式のアプリケーションを取得する必要があります。
use strict; use warnings; use Plack; use Plack::Builder; use Plack::Request; warn 'AFTER IMPORT'; sub body { return 'body'; } sub body2 { return shift; } my $app = sub { warn 'REQUEST'; my $env = shift; my $req = Plack::Request->new($env); my $res = $req->new_response(200); $res->body(body()); die("DIED"); return $res->finalize(); }; my $main_app = builder { warn 'IN BUILDER'; mount "/" => builder { $app }; };
1つのワークフローでアプリケーションを起動し、リクエストを行います。 アプリケーションは自然にクラッシュします。 しかし、結果は奇妙ですが、論理的です。 アプリケーションはクラッシュせず、STDERRには2つの通知のみが表示されました。
REQUEST at /home/noxx/perl/psgi/app.psgi line 16. DIED at /home/noxx/perl/psgi/app.psgi line 21.
ダイを交換します(「DIED」)。 1番出口で。 Starmanを実行し、localhost:8080 /にリクエストを送信します。 ワークフローは落ちました。 これはSTDERRから見ることができ、次のようになります。
REQUEST at /home/noxx/perl/psgi/app.psgi line 16, <$read> line 7. AFTER IMPORT at /home/noxx/perl/psgi/app.psgi line 7, <$read> line 8. IN BUILDER at /home/noxx/perl/psgi/app.psgi line 26, <$read> line 8.
各リクエストの後、ワークフローは落ちますが、マスタープロセスはそれを上げます。
しばらくスターマンを離れる。 たとえば、Twiggyの下でこのアプリケーションを実行してみましょう。 このサーバーがインストールされていない場合は、インストールします。 パッケージはTwiggyと呼ばれます。
Twiggyをインストールしたら、次のコマンドでアプリケーションを実行します。
twiggy --port 8080 app.psgi
そして、リクエストを行います。 1つの機能を除き、すべてスターマンに似ています。 サーバーが落ちました。
noxx@noxx-inferno ~/perl/psgi $ twiggy --port 8080 app.psgi AFTER IMPORT at /home/noxx/perl/psgi/app.psgi line 7. IN BUILDER at /home/noxx/perl/psgi/app.psgi line 26. REQUEST at /home/noxx/perl/psgi/app.psgi line 16, <> line 5. noxx@noxx-inferno ~/perl/psgi $
もちろん、これはTwiggyにはマスタープロセスがなく、倒れたワーカーを育てる人がいないためです。 そして、ここから非常に重要な点が続きます。これを考慮に入れなければなりません。 サーバーを再起動する前に、コードが正しく、構文エラーが含まれていないことを確認する必要があります。 Starmanを使用してエラーを含むアプリケーションを起動しようとすると、いくつかのイベントが次の順序で発生します。
- スターマンはマスタープロセスを開始し、ワークフローを開始できるかどうかを確認します。
- Starmanはワークフローを開始し、アプリケーションコードを実行に渡します。
- ワークフローが落ち始め、マスターがそれらを拾い始めます。
- 負荷は非常に短時間で増加します。
実行時エラーはそれほど重大ではありません。 アプリケーションからドロップを削除して、ほぼ初期状態に戻しましょう。
use strict; use warnings; use Plack; use Plack::Builder; use Plack::Request; warn 'AFTER IMPORT'; sub body { return 'body'; } sub body2 { return shift; } my $app = sub { warn 'REQUEST'; my $env = shift; my $req = Plack::Request->new($env); my $res = $req->new_response(200); $res->body(body()); return $res->finalize(); }; my $main_app = builder { warn 'IN BUILDER'; mount "/" => builder { $app }; };
そして、この順序で次のことを試してください。
- アプリケーションを元の形式にします。
- Starmanを使用して実行します。
- リクエストをします。
- アプリケーションコードを変更して保存します。
- アプリケーションを再起動せずに、再度リクエストを行います。
結果:
curl localhost:8080/ body
アプリケーションを保存し、本体機能を変更します。 ここで、たとえば、誰も返さないとします。 要求を行います-サーバーを再起動しなかった場合の結果は次のとおりです。
curl localhost:8080/ body
ただし、すべてが変更されるため、再起動する価値があります。
curl localhost:8080/ nobody
別の重要な結論。 アプリケーションを更新するには、ファイルを変更するだけでは不十分です。 サーバーを再起動する必要があります。 または、マスタープロセスに特別なシグナルを送信します。
スターマンと信号
停止できない大規模なPSGIアプリケーションがあると想像してください。 メモリにロードするかなり重いライブラリ、たとえば10秒があります。
前の一連のアクションを繰り返しますが、1つの変更があります。 信号の送信を追加します。
再読するようにスターマンに伝える信号はSIGHUPです。
この信号を送信するコマンドは次のようになります。
kill -s SIGHUP [pid]
次のコマンドでpid値を取得できます。
ps uax | grep starman | grep master
コマンド出力の例:
noxx 6214 0.8 0.1 54852 10288 pts/3 S+ 19:17 0:00 starman master --port 8080 --workers 1 app.psgi
pid = 6214。
要求と応答を確認してください。 誰もbodyに戻してアプリケーションを実行しません。
結果:
curl localhost:8080 body kill -s SIGHUP 6214 curl localhost:8080 nobody
一方、STDERR Starmanでは次のことがわかります。
AFTER IMPORT at /home/noxx/perl/psgi/app.psgi line 7. IN BUILDER at /home/noxx/perl/psgi/app.psgi line 24. REQUEST at /home/noxx/perl/psgi/app.psgi line 16. Sending children hup signal AFTER IMPORT at /home/noxx/perl/psgi/app.psgi line 7, <$read> line 2. IN BUILDER at /home/noxx/perl/psgi/app.psgi line 24, <$read> line 2. REQUEST at /home/noxx/perl/psgi/app.psgi line 16, <$read> line 2.
したがって、PSGIアプリケーションを更新するには2つの方法があります。 どちらを選択するかは、タスクによって異なります。
別のワークフローが必要だとします。 2つの方法で追加できます。 必要なパラメーター(--workers)を使用してサーバーを再始動するか、シグナルを送信します。 1つのワークフローを追加するシグナルはTTINであり、削除するシグナルはTTOUです。 サーバーを安全に完全に停止したい場合は、QUITシグナルを使用できます。
だから。 1つのワークフローでアプリケーションを実行します。
starman --port 8080 --workers 1
次に、次のコマンドを2回実行して2つのプロセスを追加します。
kill -s TTIN 6214
スターマンプロセスリスト:
noxx 6214 0.0 0.1 54852 10304 pts/3 S+ 19:17 0:00 starman master --port 8080 --workers 1 app.psgi noxx 6221 0.0 0.1 64724 13188 pts/3 S+ 19:19 0:00 starman worker --port 8080 --workers 1 app.psgi noxx 6233 0.0 0.1 64476 12872 pts/3 S+ 19:26 0:00 starman worker --port 8080 --workers 1 app.psgi noxx 6239 2.0 0.1 64480 12872 pts/3 S+ 19:29 0:00 starman worker --port 8080 --workers 1 app.psgi
STDERRはすでにおなじみです:
AFTER IMPORT at /home/noxx/perl/psgi/app.psgi line 7, <$read> line 4. IN BUILDER at /home/noxx/perl/psgi/app.psgi line 24, <$read> line 4. AFTER IMPORT at /home/noxx/perl/psgi/app.psgi line 7, <$read> line 4. IN BUILDER at /home/noxx/perl/psgi/app.psgi line 24, <$read> line 4.
次に、1つのプロセスを削除します。
kill -s TTOU 6214
プロセスのリストを見ると、チームが効果を上げていることがわかります。
noxx 6214 0.0 0.1 54852 10304 pts/3 S+ 19:17 0:00 starman master --port 8080 --workers 1 app.psgi noxx 6221 0.0 0.1 64724 13188 pts/3 S+ 19:19 0:00 starman worker --port 8080 --workers 1 app.psgi noxx 6233 0.0 0.1 64476 12872 pts/3 S+ 19:26 0:00 starman worker --port 8080 --workers 1 app.psgi noxx 6238 0.0 0.0 13584 936 pts/4 S+ 19:29 0:00 grep --colour=auto starman
ただし、STDERRではこれは表示されません。
そして、QUITシグナルを送信することにより、アプリケーションの作業を完了します。
kill -s QUIT 6214
サーバーはSTDERRに書き込みます。
2013/06/02-19:32:15 Received QUIT. Running a graceful shutdown Sending children hup signal 2013/06/02-19:32:15 Worker processes cleaned up 2013/06/02-19:32:15 Server closing!
シャットダウンします。
Starmanについて知る必要があるのはこれだけです。
別の重要な詳細が残っています。 Starmanを起動すると、-Mスイッチを使用して必要なモジュールを指定し、マスタープロセスを起動できます。 しかし、その後、次の制限が機能し始めます。 -M(-MDBI -MDBIx :: Class)を介してロードされたモジュールは、SIGHUPで再読み込みされません。
別の便利なサーバーオプションは-Iです。 マスタープロセスを開始する前に、Perlモジュールへのパスを指定できます。 StarmanはUnixソケットでも動作しますが、この機能については、Plackの展開と管理に関する記事から始めて、次の記事で詳しく説明します。
そして最後に、環境変数(PLACK_ENV)を渡された状態に設定する-Eフラグ。
次の記事では、非同期PSGIサーバー-Twiggyを取り上げます。
ドミトリー・シャマトリン