NginxでPSGI/Plack(Starlet)でHello world
PSGI/Plackとは
mod_perlやFastCGIと同じく、モジュールをオンメモリで永続化することによって処理の高速化を図る仕組み。サーバ(デーモン)です。
似たコンセプトの仕組みがいくつかある現状、特にフレームワーク側でそれぞれ別の接続対応をする必要があり、コネクタ部分の無駄な開発が繰り返されていました。
そこでマルチコネクタ的なものを作って、インターフェイス部分を吸収しようということで生まれたのがPSGIという規格らしいです。
第1回 PSGI/Plack―フレームワークとサーバをつなぐエンジン (1):Perl Hackers Hub|gihyo.jp … 技術評論社
PSGIを使えば上記のmod_perlやFastCGIなどを独自設定を気にせず使えるようになるのですが、せっかくだからPSGIだけの環境で実行できる、perlネイティブのサーバがあるといいよね、ということで生まれたのがPlackとのこと。
Plackはあくまでリファレンス(参考)実装として作られたらしいのですが、付属のツール類はデファクトスタンダードとなっており、本体も出来の良さからテストサーバなどでは現在でも普通に使われているらしい。
Nginxでは静的なファイルの配信しか出来ないと前回書きましたが、動的なページを配信するためにこのようなデーモンにリクエストを引き継ぎ、結果を受け取ってクライアントに返す橋渡しのような処理を行います。これをリバースプロキシというらしい。
ここから本題
やることとしては、
となります。
1.NginxのPSGI向け設定
PSGI(Starlet)とNginxを繋ぐ方法は、TCP接続とUNIXドメインソケット接続の2通りあります。
調べなきゃ寝れない!と調べたら余計に寝れなくなったソケットの話 - Qiita
例外もあるらしいのですが(Solaris)、UNIXドメインソケット通信のほうが圧倒的に早い。
ということでこちらで繋ぐようにします。
Nginx側でいじるのは二箇所だけです。
前回記事の「/srv/www/nginx.conf」。バーチャルホストの設定部分。
vim /srv/www/nginx.conf
upstream 192.168.0.10 { #(b) server unix:/tmp/192.168.0.10.sock; } server { listen 80 default_server; server_name 192.168.0.10; access_log /srv/www/192.168.0.10/logs/access.log main; error_log /srv/www/192.168.0.10/logs/error.log warn; etag off; location / { proxy_set_header Host $host; #(a) proxy_pass http://192.168.0.10; } }
(a) proxy_set_header、proxy_pass
ソケット用の設定です。
Starlet + Server::Stater で UNIX domain socketに対応しました - Hateburo: kazeburo hatenablog
2.Plack、Starletのインストール
CPANの設定で「依存関係のあるモジュールを全てインストールする」としていれば、
cpan install Starlet
これでPlack(ツール類含む)からStarletからまで全てインストールされます。
この上なくかんたんです。
また、必須ではないのですが下記をインストールします。
cpan install Linux::Inotify2 #(a) cpan install HTTP::Parser::XS #(b) cpan install Router::Simple #(c)
(b) Starletのパフォーマンス向上
あるとパフォーマンスが上がるらしい。
必須ではないので依存関係がなく、自動ではインストールされない。
第24回 PSGI/Plack実践入門―Starman,Starlet,Twiggy,Plack::Middleware,Server::Starter(2):Perl Hackers Hub|gihyo.jp … 技術評論社
(c) ルータ、ディスパッチャー
普通の?apache&cgiだとURLによって処理するCGIが変わりますが、PSGIの場合常駐スクリプト(daemon)が一人で窓口を担当することになります。
リクエストURLと対応するモジュールの紐付けが必要です。
フレームワークを使わない前提だと、該当機能を担う、いわゆるディスパッチャーがありません。
手法は幾つかあり、PSGI提唱者のmiyagawaさんがサンプルを公開されています。
GitHub - miyagawa/plack-dispatching-samples: Examples of Plack dispatcher using various CPAN modules
今回はその中からRouter::Simpleを使ってみました。
3.最低限のコーディング
前回と同様、ドキュメントルートは/srv/www/192.168.0.10/htmlの想定です。
Nginxにおけるドキュメントルートの設定は、すなわち静的なファイルの場所ということなのであんまり関係ないのですが、スクリプトを/srv/www/192.168.0.10/の下に配置しています。
Starletサーバの起動スクリプトを書く
コマンドラインで叩いてサーバ(daemon)を起動させるわけですが、コマンドの一部をシェルスクリプトに書き出しています。
ホットデプロイ時にサーバ自体の設定を変えたい場合に、この書き方をしていると可能になるらしいです。
PSGI/Plackアプリケーションの起動方法いろいろと本番環境アレコレ - blog.nomadscafe.jp
vim /srv/www/192.168.0.10/scripts/run.sh
#!/bin/bash exec plackup -s Starlet \ --spawn-interval 0.1 --max-workers 10 \ --max-reqs-per-child 5000 --min-reqs-per-child 4000 \ -a /srv/www/192.168.0.10/scripts/index.psgi
PSGIのフロントとなるスクリプトを書く
ここで追加インストールしたRouter::Simpleを使っています。
ちゃんと作る場合、ルーティングの設定は切り出したほうがいいでしょう。
vim /srv/www/192.168.0.10/scripts/index.psgi
#!/usr/bin/perl BEGIN { push @INC, ('/srv/www/192.168.0.10/lib/'); } use strict; use warnings; use Plack::Request; use Plack::Response; use Router::Simple; use MyTest; my $router = Router::Simple->new(); $router->connect('/', { controller => 'MyTest', action => 'index' }, { method => 'GET' }); return sub { my $env = shift; my $p = $router->match($env) || return Plack::Response->new(404)->finalize; my $req = Plack::Request->new($env); my $controller = $p->{controller}; my $action = lc($req->method) . '_' . $p->{action}; my $res = $controller->can($action)->($req, $p); $res->finalize; };
この例では「/」にアクセスされた場合に、MyTestモジュールのget_indexというメソッドを呼び出す設定をしています。
Router::Simpleのmethodはオプション引数なので、そこまではいらないって場合は取ってしまってもいいかも。
取ろうと思えば$req->methodでとれますしね。
Hello world用モジュール
vim /srv/www/192.168.0.10/lib/MyTest.pm
package MyTest; use utf8; use Encode; sub get_index { my ($req, $p) = @_; my $res = Plack::Response->new(); my $html = "Hello world."; $res->status(200); $res->headers({'Content-Type' => 'text/html'}); $res->body( encode_utf8($html) ) ); return $res; }
ここまででモジュールの用意は完了です。
Starletの起動
起動用モジュールを絡めて起動します。
start_server --signal-on-hup=USR1 --path /tmp/192.168.0.10.sock -- /srv/www/192.168.0.10/scripts/run.sh
これで192.168.0.10にアクセスすると、Hello world.が表示されます。