組み込みのperlになります!

寒いシベリアの冬に、当局は熱いボスティアの愛で私たちのチームを暖めることに決め、別のプロジェクトを私たちの手に渡しました。 一見すると、いくつかの重大な問題が明らかになりました。

一般に、本物のロシア人と同じように、私たちは犬のクソのためにすべてを分解し、それを美しく再構築することにしました。 カブをひっかいた後、彼らはapache + modperlはベテランの人向けであり、面白くないと判断しました。fastcgiは意志の弱い人の方法です。

後で私はグラバーで異なるサイズの密に播種されたフィールドに感じました...


ジェダイ・ウェイ

文字通り最初のコード行で、最初の問題が始まりました。 まず、私たちのお気に入りの真珠モジュールの多くは、Apacheの下で研ぎ澄まされ、どれだけ説得してもnginxの下で動作することを拒否しました。 さらに、フォーラムには「nginx + perl + mason-これは不可能です」などのヘッダーがいっぱいで、すぐに警告されました。 無駄になったように! メイソンはハーフキックで巻き上げてうまくいったが、それは後であった。

私は本当にnginxの下のモジュールをシャープにしたくありませんでした(上のロシア人に関するコメントを参照)ので、自分で書くことにしました。 それをすべて捨てる
不要なのは、Apache :: Sessionだけが欠落していることでした。名前が示すように、これは敵のサーバーの下でのみセッションで機能します。 Apacheと同じインターフェースを備えたセッションストレージ::セッションは夕方に書かれ、幸福感はとても近いように見えました。

翌朝、私は実際に何が話題なのか、nginxに組み込まれた真珠の植物について始めることにしました。 そして、楽しみが始まりました。

nginxのマニュアルには、すべてが非常に単純であると書かれています。 構成に挿入する必要があります
         http {
                 perl_modules habrahabr / lib;
                 perl_require handler.pl;
                サーバー{
                        場所/ {
                                 perl habrahabr :: webhandler;
                         }
                 }
         }


そして、ハンドラー自体を記述します

package habrahabr ;
use nginx ;
sub webhandler {
my $r = shift ;
$r -> send_http_header ( "text/html" );
return OK if $r -> header _ only ;
$r -> print ( "hello habrahabr!\n<br/>" );
return OK ;
}
1 ;


configのパスについて少し祈ってから、この例を開始しました。 次に、石工を開始します。 その後、最初のレーキが必要な場所で打撃を受けました。通常、メイソンはHTML :: Mason :: ApacheHandlerを介して行われます。 私たちにはこれがなかったので、メイソンの根底に行き、HTML ::メイソン:: Interpを直接使用する必要がありました。 ただし、デフォルトでは、すべての出力が標準出力に出力され、$ r-> print()で必要になります。 喫煙マナ
コンストラクターHTML :: Mason :: Interpパラメーターout_methodを見ました。 熊手は次のように表示されます:メイソンはかなり重いものであり、ページが要求されるたびに作成するのは面倒ではありません。したがって、初期化中に1つのメイソンオブジェクトを作成し(ここでも、レイクですが、その詳細は後で説明します)、後ですべてのリクエストを処理するために使用しますおよび$ rグローバル!
どういうわけかこのようになった:

our $r ;
our $mason ;

$mason = new HTML :: Mason :: Interp (
...
out _ method => sub { $r -> print ( @ _ ) } ,
...
);

sub webhandler {
$r = shift ;
...
$mason -> exec ( $r -> uri );
}


その直後、nginxはURLから変数にパラメーターを渡すことはできず、URL自体だけを渡すことができました。 まあ、私は、損失はそれほど大きくないと思った。 URLを解析し、すべてを慎重に変数に入れて続行しました。

もちろん、近い将来、GETではなくPOSTリクエストを使用する必要があり、それらはnginxで非常にうまく処理されます。 マニュアルで判断すると、POST要求が到着した場合、$ r-> has_request_body(handler_ref)を介してハンドラーを設定することで処理できます。
最初のハンドラーは次のようなものでした:
sub HandlePOST {
my $body = $r -> request _ body ;
...
DoRequest ();
return OK ; # - ,
# , webhandler , OK
}


しかし、自己認識の短い読書の後、それはすぐに変わりました
sub HandlePOST {
my $body = $r -> request _ body ;
unless ( $body ) {
my $file = $r -> request _ body _ file ;
if ( $file && - f $file ) {
my ( $fh , $block );
open $fh , "<$file" ;
binmode $fh ; #
while ( read $fh , $block , 4096 ) { $body .= $block }
close $fh ;
}
}
...
DoRequest ();
}


パラメータを渡さずに空白のページでバング...と動作しました。 ほんの少し-すぐにシグナル11で終了した美しいワーカープロセスがログに表示されました。 マナの控除は役に立たなかった。 デバッグは、常に異なる方法で行われることが確立されました-時には$ r-> request_bodyを呼び出すとき、時には$ r-> request_body_file、時にはDoRequestに、時にはDoRequestの後に。 2日後(!!!)目に涙を浮かべて、マニュアルを1000回開いた。目が1本の線につかまり、私の心臓はひどい力で鼓動し、唇は私が知っているすべての印刷できない呪文を静かに繰り返した。

すべてがいつものように非常にシンプルであることが判明しました。webhandlerで$ r = shift; 他の場所で同じオブジェクトを使用してはいけません! HandlePOSTでは、リクエストオブジェクトも渡されますが、これは既にグローバルオブジェクトとは完全に異なる場合があります。 まあ、それは古典的なセグメンテーション違反であることが判明しました。

行を追加した後$ r = shift; HandlePOSTの最初に すべてが問題なく機能しました。

ほぼ最後に、ファイルのダウンロードに少し手を加えなければなりませんでした。 私たちはCGIを使用しなかったので、そこですべての種類のgoogleを読む必要がありました。ハンドラーで、ファイルとそれに関連するものがあることに気付きました。 サービスの結果として、RFC822、RFC2822、RFC2046-2049は長い間読まれ、暗記されてきたので、ファイルが通常のMIMEパートとして入っていることがすぐにわかりました。 私は自分の手でそれを解析することを敢えてしなかったので、MIME :: Parserで解析しようとしました。 解析、感染!

私が書くことを約束したが、適切な場所を見つけることができなかった別の機能:
起動時に、nginxは真珠を起動し、ハンドラーでファイルを読み込みます。 この真珠ファイルは正直に実行されます(つまり、すべてのuse、require、非手続き型コードが実行されるなど)。 このすべての幸福がroot'a =)から作られていることを除いて、大丈夫のようです。 一般に、メイソンでは、主な問題がここで発生しました。テンプレートを毎回再コンパイルしないように、独自のキャッシュを持っています。 キャッシュへのパスはコンストラクターで指定されますが、このパスは完全ではなく、その内部の石工は多数のサービスフォルダーとファイルを作成します。 さらに、リクエストがクライアントから送信されると、webhandlerは管理者によって与えられた権限(つまり、nginx:nginx)で既に実行され、フォルダーがルートとして作成されたため、コンパイルされたテンプレートをキャッシュに書き込むことができません。

2つの方法がありました:メイソンオブジェクトを作成するとき、通常のユーザーのフォルダーのchmod-sを実行するか、webahndlerに既にメイソンオブジェクトを作成します(最初は本当に避けたかった)。
2つの悪のうち、2番目の悪を選択しましたが、それよりも少し低いと、それがどのように実現されたかが明らかになります。

数週間後、ローンチが行われ、すべてが順調でした。nginxの最初の100万件の成功した回答(休憩なし)を完全な勝利として受け取りました。 グッドウォン!

最後に、マナを読むことは(特に母国語の場合)読んでおくと便利ですが、すぐに必要なものが見つかるとは限りません。 さて、masonを使用し、会ったすべてのレーキをバイパスするnginxハンドラーのスケルトンを紹介します。

package Habrahabr ;
use strict ;
use warnings ;
use nginx ;
use HTML :: Mason ;

# our - ,
our $request ;
our $mason ;
our $init = 0 ;
sub WebHandler {
$request = shift ;
if ( ! $init || ! $mason ) {
$mason = new HTML :: Mason :: Interp (
comp _ root => "/opt/habrahabr/html" ,
data _ dir => "/opt/habrahabr/var/mason_cache/" ,
use _ strict => 1 ,
out _ method => sub { $request -> print ( @ _ ) } ,
error _ format => 'html' ,
error _ mode => 'output' ,
allow _ globals => [ qw ( $request %session $uri $post_param ) ], #
);
}
#
$mason -> set_global ( uri => '' );
$mason -> set_global ( post _ param => '' );
if ( $r -> request _ method eq 'POST' ) {
$request -> has_request_body ( \& Habrahabr :: ProcessPOST );
# !
}
elsif ( $r -> request _ method eq 'GET' ) {
DoRequest ();
}
else {
return DECLINED ;
}
return HTTP_OK ;
}
sub ProcessPOST {
$request = shift ; #
# post
my $body = $r -> request _ body ;
unless ( $body ) {
my $file = $r -> request _ body _ file ;
if ( $file && - f $file ) {
my ( $fh , $block );
open $fh , "<$file" ;
binmode $fh ;
while ( read $fh , $block , 4096 ) { $body .= $block } ;
close $fh ;
}
}
#
my $ ContentType = $r -> header_in ( 'Content-type' );
if ( $ ContentType =~ 'multipart/form-data' ) {
my ( $ Boundary ) = ( $ ContentType =~ / boundary = "?([^\s]+)" ?/ );
# , , MIME
$body = "Content-Type: multipart/form-data;\n boundary=\"$Boundary\"\n\n" . $body ;
my $ Parser = new MIME :: Parser ;
$ Parser -> output_to_core ( 1 );
$ Parser -> decode_bodies ( 0 );
# . . eval , eval !
eval {
$body = $ Parser -> parse_data ( $body );
} ;
}

$mason -> set_global ( post _ param => \ $body );
DoRequest ();
}
sub DoRequest {
my $uri = $request -> uri ;
# DirectoryIndex . nginx'a .
if ( ! $mason -> comp_exists ( $uri ) ) {
if ( $mason -> comp_exists ( $uri . "/index.html" ) ) {
$uri = $uri . "/index.html" ;
}
else {
$uri = "/404.html" ; # 404 , 200
}
}
$mason -> set_global ( request => $request );
$mason -> set_global ( uri => $uri );
$mason -> exec ( $uri );
}


私は無駄に働いていないことを願っています、そしてこのトピックは少なくとも誰かを助けるでしょう。 幸運=)

_________
VIMで作成されたテキスト。 コードをペイントしたGNUソースハイライト

UPD:私はPHPに対して何もしていません-私はそれについて多くを自分で書いていました。 真珠が私にとってもっと面白いというだけです。 また、最初の段落は、PCPが非言語的であると考えているためではありませんが、真珠について書いているので、すべてをやり直すことが決定されたため、真珠についてそれを行うことは明らかでした。

Source: https://habr.com/ru/post/J45590/


All Articles