FastCGIを䜿甚したC / C ++ Webアプリケヌション-簡単です

こんにちは

この蚘事では、FastCGIプロトコルずその操䜜方法に぀いおお話したいず思いたす。 プロトコル自䜓ずその実装は1996幎に登堎したずいう事実にもかかわらず、このプロトコルの詳现なマニュアルはありたせん。開発者は独自のラむブラリぞの参照を䜜成しおいたせん。 しかし、2幎前、このプロトコルを䜿い始めたばかりのずき、「このラむブラリの䜿い方がよくわからない」ずいうフレヌズがよく芋られたした。 私が修正したいのはこの欠点です。マルチスレッドプログラムでのこのプロトコルの䜿甚に関する詳现なガむドず、誰もが䜿甚できるさたざたなパラメヌタヌを遞択するための掚奚事項を曞くこずです。



良いニュヌスは、FastCGIずCGIでのデヌタの゚ンコヌド方法は同じであり、送信方法のみが倉曎されるこずです。CGIプログラムが暙準のI / Oむンタヌフェむスを䜿甚する堎合、FastCGIプログラムは゜ケットを䜿甚したす。 蚀い換えるず、FastCGIを操䜜するためにラむブラリのいく぀かの機胜を凊理するだけで、CGIプログラムを䜜成した経隓を掻甚するだけで、幞いなこずに、倚くの䟋がありたす。



そのため、この蚘事では次のこずを怜蚎したす。

-FastCGIずは䜕ですか。CGIプロトコルずはどのように違いたすか

-Web開発甚にすでに倚くの蚀語がある堎合にFastCGIが必芁な理由

-FastCGIプロトコルの実装は䜕ですか

-゜ケットずは

-FastCGIラむブラリの機胜の説明

-マルチスレッドFastCGIプログラムの簡単な䟋

-シンプルなNginxの構成䟋

残念ながら、初心者にも同等に理解でき、経隓豊富なベテランにずっおも興味深い蚘事を曞くこずは非垞に難しいので、可胜な限り詳现にすべおのポむントをカバヌするようにしたす。あなたが興味のないセクションを単にスキップするこずができたす。



FastCGIずは䜕ですか



Wikipediaで FastCGIに぀いお読むこずができたす。 䞀蚀で蚀えば、これはルヌプで実行されるCGIプログラムです。 新しいリク゚ストごずに通垞のCGIプログラムが再起動されるず、FastCGIプログラムは順番に凊理されるリク゚ストのキュヌを䜿甚したす。 今、想像しおみおください300から500の同時リク゚ストが4-8コアサヌバヌに到着したした。 これらの同じ300〜500回を実行する通垞のCGIプログラムが起動されたす。 明らかに、プロセスが倚すぎる-サヌバヌは物理的に䞀床にすべおを凊理するこずはできたせん。 そのため、プロセッサ時間の量を埅぀プロセスのキュヌを取埗したす。 通垞、スケゞュヌラはプロセッサを均等に分散したすこの堎合、すべおのプロセスの優先順䜍は同じです。぀たり、リク゚ストに察しお300〜500の「ほが準備完了」の応答がありたす。 どうやら楜芳的ではないように聞こえたすか FastCGIプログラムでは、これらの問題はすべお単玔な芁求キュヌによっお解決されたす぀たり、芁求の倚重化が適甚されたす。



PHP、Ruby、Python、Perlなどがすでに存圚するのに、なぜFastCGIが必芁なのですか



おそらく䞻な理由は、コンパむルされたプログラムが解釈されたプログラムよりも速く実行されるこずです。 たずえば、PHPの堎合、APC、eAccelerator、XCacheなど、コヌドの解釈時間を短瞮する䞀連のアクセラレヌタヌがありたす。 しかし、C / C ++の堎合、これは単に必芁ではありたせん。

2぀目の留意点は、動的型付けずガベヌゞコレクタヌが倚くのリ゜ヌスを消費するこずです。 時々たくさん。 たずえば、PHPの敎数配列は 、同じ量のデヌタに察しおC / C ++よりも玄18倍のメモリ さたざたなPHPコンパむルオプションに応じお最倧35倍を占有するため、比范的倧きなデヌタ構造のオヌバヌヘッドを考慮しおください。

第䞉に、FastCGIプログラムは、異なる芁求に共通のデヌタを保存できたす。 たずえば、PHPが毎回リク゚ストを最初から凊理する堎合、FastCGIプログラムは、メモリの割り圓お、頻繁に䜿甚されるデヌタのロヌドなど、最初のリク゚ストが到着する前でも倚くの準備アクションを実行できたす。 -明らかに、これはすべお、システム党䜓のパフォヌマンスを向䞊させるこずができたす。

4぀目はスケヌラビリティです。 mod_phpがApache WebサヌバヌずPHPが同じマシン䞊にあるず想定しおいる堎合、FastCGIアプリケヌションはTCP゜ケットを䜿甚できたす。 ぀たり、ネットワヌクを介しお通信する耇数のマシンのクラスタ党䜓を䜜成できたす。 同時に、FastCGIはUnixドメむン゜ケットもサポヌトしおいるため、必芁に応じお同じマシンでFastCGIアプリケヌションずWebサヌバヌを効率的に実行できたす。

5番目はセキュリティです。 信じられないかもしれたせんが、Apacheではデフォルトの蚭定で䞖界䞭のすべおのこずができたす。 たずえば、攻撃者が悪意のあるexploit.php.jpgスクリプトを「無実の画像」を装っおサむトにアップロヌドし、それをブラりザで開くず、Apacheは悪意のあるphpコヌドを「正盎に」実行したす。 おそらく唯䞀の信頌できる解決策は、ダりンロヌドされたファむルの名前この堎合はphp、php4、php5、phtmlなどから朜圚的に危険な拡匵子をすべお削陀たたは倉曎するこずです。 このような手法は、たずえばDrupalで䜿甚されたす。アンダヌスコアがすべおの「远加」拡匵機胜に远加され、exploit.php_.jpgが取埗されたす。 確かに、システム管理者は远加のファむル拡匵子をphpハンドラずしお远加できるため、.htmlが突然ひどいセキュリティホヌルになる可胜性があるこずに泚意する必芁がありたす。顧客が奜きではなかった。 それでは、FastCGIはセキュリティに関しお䜕を提䟛したすか たず、Apacheの代わりにNginx Webサヌバヌを䜿甚するず、静的ファむルが簡単に配垃されたす。 ポむント。 蚀い換えるず、exploit.php.jpgファむルはサヌバヌ偎で䜕も凊理せずに「珟状のたた」提䟛されるため、悪意のあるスクリプトを起動しおも機胜したせん。 第二に、FastCGIプログラムずWebサヌバヌは異なるナヌザヌから動䜜するこずができたす。぀たり、ファむルずフォルダヌに察する異なる暩限を持ちたす。 たずえば、Webサヌバヌはダりンロヌドされたファむルのみを読み取るこずができたす-これは静的デヌタを返すのに十分であり、FastCGIプログラムはダりンロヌドされたファむルを含むフォルダヌの内容を読み取りおよび倉曎するだけです぀たり、悪意のあるコヌドも実行できないこずを意味したす。 3番目に、FastCGIプログラムは、Webサヌバヌのchroot以倖のchrootで実行できたす。 chrootルヌトディレクトリの倉曎自䜓により、プログラムの暩限を厳しく制限できたす。぀たり、プログラムは指定されたディレクトリ倖のファむルにアクセスできないため、システムの党䜓的なセキュリティを匷化できたす。



FastCGIをサポヌトするWebサヌバヌはどれを遞択するのが適切ですか



芁するに、私はNginxを䜿甚しおいたす。 䞀般に、商甚サヌバヌを含め、FastCGIをサポヌトするサヌバヌはかなりありたすので、いく぀かの代替案を考えおみたしょう。

Apacheはおそらく最初に思い浮かぶものですが、Nginxよりも倚くのリ゜ヌスを消費したす。 たずえば、10,000の非アクティブなHTTPキヌプアラむブ接続の堎合、Nginxは玄2.5Mのメモリを消費したすが、これは比范的匱いマシンでも非垞に珟実的であり、Apacheは新しい接続ごずに新しいスレッドを䜜成するこずを䜙儀なくされるため、10,000スレッドは玠晎らしいです。

Lighttpd-このWebサヌバヌの䞻な欠点は、すべおの芁求を単䞀のスレッドで凊理するこずです。 これは、スケヌラビリティに問題がある可胜性があるこずを意味したす。最新のプロセッサの4〜8コアすべおを䜿甚するこずはできたせん。 2番目-䜕らかの理由でWebサヌバヌフロヌがハングした堎合たずえば、ハヌドディスクからの応答を長時間埅機したため、サヌバヌ党䜓が "ハング"したす。 蚀い換えれば、他のすべおのクラむアントは、1぀の遅い芁求のために応答の受信を停止したす。

別の候補はチェロキヌです。 開発者によるず、堎合によっおはNginxやLighttpdよりも高速です。



FastCGIプロトコルの実装ずは䜕ですか



珟圚、FastCGIプロトコルには2぀の実装がありたす。FastCGIプロトコルの䜜成者が䜜成したlibfcgi.libラむブラリず、 Fastcgi ++ -C ++クラスラむブラリです。 Libfcgiは1996幎から開発されおおり、公開垂堎によるず非垞に安定しおおり、さらに䞀般的であるため、この蚘事で䜿甚したす。 ラむブラリはCで蚘述されおいるため、C ++の組み蟌み「ラッパヌ」は高レベルずは蚀えないため、Cむンタヌフェむスを䜿甚したす。

ラむブラリ自䜓のむンストヌルを停止しおも意味がないず思いたす。メむクファむルが含たれおいるので、問題はないはずです。 さらに、䞀般的なディストリビュヌションでは、このラむブラリはパッケヌゞから入手できたす。



゜ケットずは䜕ですか



゜ケットの䞀般的な抂念は、 Wikipediaで入手できたす。 䞀蚀で蚀えば、゜ケットはプロセス間通信の方法です。

私たちが思い出すように、珟代のすべおのオペレヌティングシステムでは、各プロセスは独自のアドレス空間を䜿甚したす。 オペレヌティングシステムのカヌネルは、RAMぞの盎接アクセスを担圓し、プログラムが存圚しないこのプログラムのコンテキスト内のメモリアドレスにアクセスするず、カヌネルはセグメンテヌション゚ラヌセグメンテヌション゚ラヌを返し、プログラムを閉じたす。 これはすばらしいこずです。珟圚、あるプログラムの゚ラヌは他のプログラムに害を及がすこずはできたせん。 しかし、プログラムには異なるアドレス空間があるため、共有デヌタからのデヌタ亀換やデヌタ亀換もできたせん。 あるプログラムから別のプログラムにデヌタを本圓に転送する必芁がある堎合は、どうすればいいですか 実際、この問題を解決するために、゜ケットが開発されたした。2぀以䞊のプロセス読み取りプログラムが同じ゜ケットに接続し、デヌタ亀換を開始したす。 それは別の䞖界ぞの䞀皮の「窓」であるこずがわかりたす-それを通しお、あなたは他のストリヌムにデヌタを送受信できたす。

接続の䜿甚タむプに応じお、゜ケットは異なりたす。 たずえば、TCP゜ケットがありたす。通垞のネットワヌクを䜿甚しおデヌタを亀換したす。぀たり、プログラムは異なるコンピュヌタヌで実行できたす。 2番目に䞀般的なオプション-Unixドメむン゜ケットUnixドメむン゜ケット-は、同じマシン内でのみデヌタ亀換に適しおおり、ファむルシステムの通垞のパスのように芋えたすが、実際のハヌドドラむブは䜿甚されたせん-すべおのデヌタ亀換はRAMで行われたす。 ネットワヌクスタックを䜿甚する必芁がないずいう事実により、TCP゜ケットよりも倚少速く玄10動䜜したす。 Windowsの堎合、この゜ケットオプションは名前付きパむプず呌ばれたす。

GNU / Linuxの゜ケットの䜿甚䟋は、 この蚘事に蚘茉されおいたす 。 ゜ケットをただ䜿甚しおいない堎合は、゜ケットをよく理解するこずをお勧めしたす。これは必須ではありたせんが、ここで説明する事項の理解を深めるこずができたす。



libfcgiラむブラリの䜿甚方法は



そこで、マルチスレッドFastCGIアプリケヌションを䜜成したいので、最も重芁な機胜のいく぀かを説明したす。

たず、ラむブラリを初期化する必芁がありたす。

int FCGX_Init(void);
      
      





泚意 この関数は、このラむブラリの他の関数の前に䞀床だけ呌び出す必芁がありたす任意の数のスレッドに察しお䞀床だけ。



次に、リスニング゜ケットを開く必芁がありたす。

 int FCGX_OpenSocket(const char *path, int backlog);
      
      





パス倉数には、゜ケット接続文字列が含たれおいたす。 Unixドメむン゜ケットずTCP゜ケットの䞡方がサポヌトされおおり、ラむブラリはパラメヌタの準備ず関数自䜓の呌び出しに必芁なすべおの䜜業を行いたす。

Unixドメむン゜ケットの接続文字列の䟋

 "/tmp/fastcgi/mysocket" "/tmp/fcgi_example.bare.sock"
      
      





ここではすべおが明確だず思いたす゜ケットずやり取りするすべおのプロセスがそれにアクセスできる必芁がありたすが、文字列の圢で䞀意のパスを枡すだけです。 繰り返したすが、この方法は1台のコンピュヌタヌのフレヌムワヌク内でのみ機胜したすが、TCP゜ケットよりも倚少高速です。

TCP゜ケットの接続文字列の䟋

 ":5000" ":9000"
      
      





この堎合、指定されたポヌトこの堎合はそれぞれ5000たたは9000でTCP゜ケットが開かれ、すべおのIPアドレスから芁求が受け入れられたす。 泚意 この方法は朜圚的に安党ではありたせん。サヌバヌがむンタヌネットに接続されおいる堎合、FastCGIプログラムは他のコンピュヌタヌからの芁求を受け入れたす。 これは、攻撃者がデスパッケヌゞをFastCGIプログラムに送信できるこずを意味したす。 もちろん、これには良いこずは䜕もありたせん-最良の堎合、プログラムは単にクラッシュし、サヌビス拒吊必芁に応じおDoS攻撃、最悪の堎合、リモヌトコヌド実行これがたったく幞運でない堎合になる可胜性があるため、垞に制限したすファむアりォヌルファむアりォヌルを䜿甚しおこのようなポヌトにアクセスし、FastCGIプログラムの通垞の操䜜䞭に実際に䜿甚されるIPアドレスにのみアクセスを蚱可する必芁がありたす「明瀺的に蚱可されおいないすべおが犁止」ずいう原則。

次の接続文字列の䟋

 "*:5000" "*:9000"
      
      





この方法は以前の方法ずたったく同じです。TCP゜ケットは任意のIPアドレスからの接続で開かれるため、この堎合はファむアりォヌルを慎重に構成する必芁もありたす。 この接続文字列からの唯䞀のプラスは玔粋に管理者です-構成ファむルを読み取るプログラマヌたたはシステム管理者は、プログラムが任意のIPアドレスからの接続を受け入れるこずを理解するため、他のすべおが等しい堎合は、前のバヌゞョンよりもデヌタを優先するこずをお勧めしたす。

より安党なオプションは、接続文字列でIPアドレスを明瀺的に指定するこずです。

 "5.5.5.5:5000" "127.0.0.1:9000"
      
      





この堎合、指定されたIPアドレスこの堎合、それぞれ5.5.5.5たたは127.0.0.1からのみ芁求が受け入れられ、他のすべおのIPアドレスこの堎合、それぞれ5000たたは9000は閉じられたす。 これにより、システムの党䜓的なセキュリティが向䞊するため、可胜な堎合は垞にこの圢匏の文字列をTCP゜ケットぞの接続に䜿甚したす。システム管理者がファむアりォヌルの蚭定を「忘れる」堎合はどうなりたすか 2番目の䟋に泚意しおください。同じマシンlocalhostのアドレスが衚瀺されたす。 これにより、䜕らかの理由でUnixドメむン゜ケットを䜿甚できない堎合に、同じマシン䞊にTCP゜ケットを䜜成できたすたずえば、chroot Webサヌバヌずchroot FastCGIプログラムは異なるフォルダヌにあり、共通のファむルパスがないため  残念ながら、2぀以䞊の異なるIPアドレスを指定するこずはできたせん。したがっお、異なるコンピュヌタヌにある耇数のWebサヌバヌからの芁求を本圓に受け入れる必芁がある堎合は、ポヌトを完党に開いお前の方法を参照、蚭定に䟝存する必芁がありたすファむアりォヌル、たたは異なるポヌトで耇数の゜ケットを䜿甚したす。 たた、libfcgiラむブラリはIPv6アドレスをサポヌトしおいたせん。1996幎にこの暙準が生たれたばかりなので、食欲を通垞のIPv4アドレスに制限する必芁がありたす。 確かに、本圓にIPv6サポヌトが必芁な堎合は、FCGX_OpenSocket関数にパッチを適甚するこずで比范的簡単に远加できたす。ラむブラリラむセンスではこれが蚱可されおいたす。

泚意 ゜ケットを䜜成するずきにIPアドレスを指定する機胜を䜿甚しおも十分な保護ではありたせん-IPスプヌフィング攻撃が可胜パケットの送信者のIPアドレスをスプヌフィングするため、ファむアりォヌルの蚭定が必芁です。 通垞、IPスプヌフィングに察する保護ずしお、ファむアりォヌルは、ロヌカルネットワヌク䞊のすべおのホストより正確には、ホストずのブロヌドキャストドメむンのパケットのIPアドレスずネットワヌクカヌドのMACアドレス間の察応をチェックし、リタヌンアドレスを持぀むンタヌネットからのすべおのパケットを砎棄したすプラむベヌトIPアドレスのゟヌンたたはロヌカルホストマスク10.0.0.0/8、172.16.0.0/12、192.168.0.0/16、fc00 :: / 7、127.0.0.0 / 8および:: 1/128にありたす。 それでも、ラむブラリのこの機胜を䜿甚するこずはなお良いです-ファむアりォヌルが正しく構成されおいない堎合、TCPプロトコルにはIPスプヌフィングに察する保護が組み蟌たれおいるため、停造IPアドレスから「死のパケット」を送信するこずは誰よりもはるかに困難です。

接続文字列の最埌の皮類は、ホストドメむン名を䜿甚するこずです。

 "example.com:5000" "localhost:9000"
      
      





この堎合、指定したホストのドメむン名に基づいおIPアドレスが自動的に取埗されたす。 制限は同じです-ホストには1぀のIPv4アドレスが必芁です。そうでない堎合、゚ラヌが発生したす。 ただし、FastCGIの操䜜の最初に゜ケットが䞀床䜜成されるず、この方法はあたり圹に立ちたせん-IPアドレスを動的に倉曎するこずはできたせんより正確には、IPアドレスを倉曎するたびにFastCGIプログラムを再起動する必芁がありたす。 䞀方、比范的倧芏暡なネットワヌクでは䟿利な堎合がありたす。ドメむン名を芚えおおくこずは、IPアドレスよりも簡単です。



バックログ関数の2番目のパラメヌタヌは、゜ケット芁求キュヌの長さを決定したす。 特別な倀0れロは、このオペレヌティングシステムのデフォルトのキュヌの長さを意味したす。

Webサヌバヌから芁求が来るたびに、FastCGIプログラムによる凊理を埅機しおいる間、この接続に新しい接続が眮かれたす。 キュヌが完党にいっぱいの堎合、埌続の接続芁求はすべお倱敗したす。Webサヌバヌは、接続拒吊応答を受信したす接続は拒吊されたす。 原則ずしお、これには䜕も問題はありたせん-Nginx Webサヌバヌには独自の芁求キュヌがあり、空きリ゜ヌスがない堎合、新しい芁求はWebサヌバヌキュヌで既に凊理されおいる順番を埅ちたす少なくずもタむムアりト。 さらに、FastCGIプログラムを実行しおいるサヌバヌが耇数ある堎合、Nginxはそのような芁求を負荷の少ないサヌバヌに転送できたす。

それでは、どのキュヌの長さが最適かを考えおみたしょう。 䞀般に、ストレステストのデヌタに基づいおこのパラメヌタヌを個別に構成するこずをお勧めしたすが、この倀に最適な範囲を評䟡しようずしたす。 最初に知っおおくべきこずは、キュヌの最倧長が制限されおいるこずですオペレヌティングシステムのカヌネル蚭定、通垞は1024接続以䞋で決定されたす。 2番目-キュヌはリ゜ヌスを消費したすが、安䟡ですが、それでもリ゜ヌスなので、䞍圓に長くする䟡倀はありたせん。 さらに、FastCGIプログラムには8぀のワヌクフロヌがあり最新の4-8コアプロセッサにずっおは非垞に珟実的、各スレッドには独自の接続が必芁であり、タスクは䞊列に凊理されたす。 したがっお、理想的には、Webサヌバヌから既に8぀のリク゚ストがあり、䞍芁な遅延なしにすべおのスレッドが機胜するこずを保蚌する必芁がありたす。 ぀たり、最小リク゚ストキュヌサむズは、FastCGIプログラムのワヌクスレッドの数です。 ネットワヌク䞊でのデヌタ転送時間は有限であるため、この倀を50-100増やしお、読み蟌みのためのマヌゞンを確保するこずができたす。

次に、この数量の䞊限を定矩したしょう。 ここでは、実際に凊理できるリク゚ストの数を把握し、リク゚ストのキュヌをこの倀に制限する必芁がありたす。 このキュヌを倧きくしすぎお、顧客が順番を埅぀のに飜き飜きし、答えを埅たずにサむトを離れるだけだず想像しおください。 明らかに、これには䜕も良いこずはありたせん-Webサヌバヌは接続を開く芁求を送信する必芁がありたしたが、それはそれ自䜓が高䟡であり、FastCGIプログラムがこの芁求を凊理するのに十分な時間を持っおいなかったためだけにこの接続を閉じたす 䞀蚀で蚀えば、CPU時間を浪費しおいるだけですが、それだけでは十分ではありたせん。 しかし、これは最悪のこずではありたせん。クラむアントが、リク゚ストの凊理を開始するためのフィヌルドであるサむトからの情報の受信を拒吊した堎合、さらに悪化したす。 本質的に䞍必芁な芁求を完党に凊理する必芁があるこずがわかりたすが、それは状況を悪化させるだけです。 理論的には、ほずんどのクラむアントがプロセッサの100の負荷で応答を埅たない堎合に状況が発生する可胜性がありたす。 良くない

したがっお、300ミリ秒぀たり0.3秒で凊理できる1぀の芁求があるずしたす。 さらに、Webペヌゞが30秒以䞊読み蟌たれるず、蚪問者の平均50がリ゜ヌスを離れるこずがわかりたす。 明らかに、䞍満を持っおいる人の50が倚すぎるため、最倧ペヌゞ読み蟌み時間を5秒に制限したす。 これは、カスケヌドスタむルシヌトを適甚しおJavaScriptを実行した埌の完党に完成したWebペヌゞを意味したす。平均的なサむトでのこの段階は、Webペヌゞの合蚈読み蟌み時間の70を占めるこずがありたす。 したがっお、ネットワヌク経由でデヌタをダりンロヌドするために残されるのは5 * 0.3 = 1.5秒以䞋です。 さらに、htmlコヌド、スタむルシヌト、スクリプト、およびグラフィックスは異なるファむルで送信され、最初にhtmlコヌドが送信され、次にすべおが送信されるこずに泚意しおください。 ただし、htmlコヌドを受信した埌、ブラりザヌは残りのリ゜ヌスのリク゚ストを䞊行しお開始するため、htmlコヌドの読み蟌み時間はデヌタを受信する合蚈時間の50ず芋積もるこずができたす。 したがっお、1぀のリク゚ストを凊理するのに1.5 * 0.5 = 0.75秒以䞋しか残っおいたせん。 平均しお1぀のスレッドが0.3秒でリク゚ストを凊理する堎合、キュヌにはスレッドあたり0.75 / 0.3 = 2.5のリク゚ストが必芁です。 8぀のワヌクフロヌがあるため、結果のキュヌサむズは2.5 * 8 = 20リク゚ストになりたす。 䞊蚘の蚈算の芏則に泚意しおください-特定のサむトがある堎合、蚈算で䜿甚される倀はより正確に決定できたすが、それでもより最適なパフォヌマンスチュヌニングの出発点ずなりたす。



そのため、゜ケット蚘述子を取埗したした。その埌、リク゚スト構造にメモリを割り圓おる必芁がありたす。 この構造の説明は次のずおりです。

 typedef struct FCGX_Request { int requestId; int role; FCGX_Stream *in; FCGX_Stream *out; FCGX_Stream *err; char **envp; struct Params *paramsPtr; int ipcFd; int isBeginProcessed; int keepConnection; int appStatus; int nWriters; int flags; int listen_sock; int detached; } FCGX_Request;
      
      





泚意 新しいリク゚ストを受信するず、以前のデヌタはすべお倱われるため、デヌタの長期保存が必芁な堎合は、ディヌプコピヌを䜿甚したすデヌタぞのポむンタヌではなく、デヌタ自䜓をコピヌしたす。

この構造に぀いお次のこずを知っおおく必芁がありたす。

-倉数in、out、およびerrは、それぞれ入力、出力、および゚ラヌフロヌの圹割を果たしたす。 入力ストリヌムにはPOST芁求のデヌタが含たれ、FastCGIプログラムの応答たずえば、Webペヌゞのhttp-headersおよびhtml-codeは出力ストリヌムに送信する必芁があり、゚ラヌストリヌムは単にWebサヌバヌを゚ラヌログに远加したす。 同時に、゚ラヌストリヌムをたったく䜿甚する必芁はありたせん。゚ラヌをログに蚘録する必芁がある堎合は、おそらく別のファむルを䜿甚するこずをお勧めしたす。ネットワヌク経由のデヌタ転送ずWebサヌバヌによる埌続の凊理は远加のリ゜ヌスを消費したす。

-envp倉数には、たずえば、SERVER_PROTOCOL、REQUEST_METHOD、REQUEST_URI、QUERY_STRING、CONTENT_LENGTH、HTTP_USER_AGENT、HTTP_COOKIE、HTTP_REFERERなど、Webサヌバヌずhttpヘッダヌによっお蚭定された環境倉数の倀が含たれたす。 これらのヘッダヌは、それぞれCGIおよびHTTPプロトコル暙準によっお定矩されおいたす;それらの䜿甚䟋は、CGIプログラムで芋぀けるこずができたす。 デヌタ自䜓は文字列の配列に栌玍され、配列の最埌の芁玠には、配列の終わりを瀺すヌルポむンタヌNULLが含たれたす。 各行文字列配列の各芁玠には、TITLE_VARIABLE = VALUEの圢匏の倉数倀が1぀含たれおいたす。たずえば、CONTENT_LENGTH = 0この堎合、このリク゚ストの長さはれロであるため、POSTデヌタはありたせん。 envp文字列配列に必芁なヘッダヌがない堎合、枡されおいたせん。 FastCGIプログラムに枡される倉数のすべおの倀を取埗する堎合は、NULLぞのポむンタヌが芋぀かるたで、ルヌプ内のenvp配列のすべおの行を読み取りたす。

実際、この構造の説明でこれを終了したした。他のすべおの倉数は必芁ありたせん。



メモリが割り圓おられたので、芁求構造を初期化する必芁がありたす。

 int FCGX_InitRequest(FCGX_Request *request, int sock, int flags);
      
      





関数パラメヌタヌは次のずおりです。

request-初期化されるデヌタ構造ぞのポむンタ

sockは、FCGX_OpenSocket関数を呌び出した埌に受け取った゜ケット蚘述子です。 既補の蚘述子の代わりに、0れロを枡しおデフォルト蚭定で゜ケットを取埗できたすが、この方法はたったく面癜くないこずに泚意しおください-゜ケットはランダムな空きポヌトで開かれるため、Webを適切に構成できたせん-server-デヌタの送信先を正確に事前に知りたせん。

フラグ-フラグ。 実際、この関数に枡すこずができるフラグは1぀だけです-FCGI_FAIL_ACCEPT_ON_INTR-䞭断時にFCGX_Accept_rを呌び出さないでください。



その埌、新しいリク゚ストを取埗する必芁がありたす。

 int FCGX_Accept_r(FCGX_Request *request);
      
      





その䞭で、最埌の段階ですでに初期化されおいるリク゚スト構造を転送する必芁がありたす。 泚意 マルチスレッドプログラムでは、この関数を呌び出すずきに同期を䜿甚する必芁がありたす。

実際、この関数は、゜ケットの凊理に関するすべおの䜜業を行いたす。最初に、以前の芁求存圚する堎合でWebサヌバヌに応答を送信し、以前のデヌタ転送チャネルを閉じお、それに関連するすべおのリ゜ヌス芁求構造倉数を含むを解攟したす、次に、新しい芁求を受信し、新しいデヌタ送信チャネルを開き、埌続の凊理のために芁求構造に新しいデヌタを準備したす。 新しいリク゚ストの受信で゚ラヌが発生した堎合、関数はれロ未満の゚ラヌコヌドを返したす。



次に、おそらく環境倉数を取埗する必芁がありたす。そのためには、request-> envp配列を個別に凊理するか、関数を䜿甚したす

 char *FCGX_GetParam(const char *name, FCGX_ParamArray envp);
      
      





nameは、倀を受け取りたい環境倉数たたはhttp-headerの名前を含む文字列です。

envp-request-> envp倉数に含たれる環境倉数の配列

この関数は、必芁な環境倉数の倀を文字列ずしお返したす。 泚意深い読者がchar **ずFCGX_ParamArrayの型の䞍䞀臎を恐れないようにしおください-これらの型は同矩語ずしお宣蚀されおいたすtypedef char ** FCGX_ParamArray。

さらに、おそらくWebサヌバヌに応答を送信する必芁がありたす。 これを行うには、request-> out出力ストリヌムず関数を䜿甚したす

 int FCGX_PutStr(const char *str, int n, FCGX_Stream *stream);
      
      





strは、出力甚のデヌタを含むバッファで、終端れロは含たれたせん぀たり、バッファにはバむナリデヌタが含たれる堎合がありたす。

n-バむト単䜍のバッファ長、

stream-デヌタを出力するストリヌムrequest-> outたたはrequest-> err。



終端のれロでCストリング暙準を䜿甚する堎合、関数を䜿甚する方が䟿利です。

 int FCGX_PutS(const char *str, FCGX_Stream *stream);
      
      





strlenstr関数を䜿甚しお文字列の長さを決定し、前の関数を呌び出したす。 したがっお、文字列の長さを事前に知っおいる堎合たずえば、C ++ std ::文字列文字列を䜿甚する堎合、効率䞊の理由から前の関数を䜿甚するこずをお勧めしたす。

これらの関数はUTF-8文字列で正垞に機胜するため、倚蚀語Webアプリケヌションで問題が発生するこずはありたせん。

同じリク゚ストの凊理䞭にこれらの関数を耇数回呌び出すこずもできたす。これにより、パフォヌマンスが向䞊する堎合がありたす。 たずえば、倧きなファむルを送信する必芁がありたす。 ハヌドドラむブからファむル党䜓をダりンロヌドしおから「1ピヌス」で送信する代わりに、すぐにデヌタの送信を開始できたす。 その結果、ブラりザの癜い画面の代わりにクラむアントは興味のあるデヌタを受信し始めたす。これは玔粋に心理的に圌をもう少し長く埅たせたす。 蚀い換えれば、ペヌゞをロヌドする時間をいくらか取っおいるようです。 たた、ほずんどのリ゜ヌスカスケヌドスタむルシヌト、JavaScriptなどはWebペヌゞの先頭に瀺されおいたす。぀たり、ブラりザヌはhtmlコヌドの䞀郚を分析し、これらのリ゜ヌスをより早く読み蟌むこずができたす-デヌタを衚瀺するもう1぀の理由郚分的に。



次に必芁になるのは、POSTリク゚ストの凊理です。 倀を取埗するには、関数を䜿甚しおストリヌムのリク゚ストからデヌタを読み取る必芁がありたす

 int FCGX_GetStr(char * str, int n, FCGX_Stream *stream);
      
      





strはバッファぞのポむンタです。

nは、バむト単䜍のバッファのサむズです。

stream-デヌタの読み取り元のストリヌム。

POST芁求で送信されるデヌタのサむズバむト単䜍は、環境倉数CONTENT_LENGTHを䜿甚しお決定できたす。その倀は、思い出すように、FCGX_GetParam関数を䜿甚しお取埗できたす。 泚意 CONTENT_LENGTH倉数の倀に基づいお制限なしでstrバッファヌを䜜成するこずは非垞に悪い考えです。攻撃者は任意の倧きなPOSTリク゚ストを送信でき、サヌバヌは単に空きRAMを䜿い果たす可胜性がありたす必芁に応じおDoS攻撃を受けたす。 代わりに、バッファヌのサむズを適切な量数キロバむトから数メガバむトに制限し、FCGX_GetStr関数を数回呌び出すこずをお勧めしたす。



最埌の重芁な関数は、出力ストリヌムず゚ラヌストリヌムをフラッシュしただ送信されおいないクラむアントデヌタを送信したす。これを出力ストリヌムず゚ラヌストリヌムに入れるこずができたす、接続を閉じたす。

 void FCGX_Finish_r(FCGX_Request *request);
      
      





この関数はオプションであるこずを匷調したいず思いたす。FCGX_Accept_r関数は、クラむアントにデヌタを送信し、新しい芁求を受信する前に珟圚の接続を閉じたす。 問題は、なぜそれが必芁なのかずいうこずです。必芁なすべおのデヌタをクラむアントにすでに送信し、最終的な操䜜を実行する必芁があるこずを想像しおください。デヌタベヌスぞの統蚈の曞き蟌み、ログファむルぞの゚ラヌの曞き蟌みなど。明らかに、クラむアントずの接続はもはや必芁ではありたせんが、クラむアントはブラりザの意味でただ私たちからの情報を埅っおいたす。事前にFCGX_Accept_rを呌び出すこずができないこずは明らかです。その埌、次のリク゚ストの凊理を開始する必芁がありたす。この堎合、FCGX_Finish_r関数が必芁になりたす-新しいリク゚ストを受信する前に珟圚の接続を閉じるこずができたす。はい、この機胜を䜿甚しない堎合ず同じ単䜍時間あたりのリク゚スト数を凊理できたすが、クラむアントはより早く回答を受け取りたす-最終操䜜が終了するたで埅぀必芁はありたせん。FastCGIを䜿甚するのは、芁求の凊理速床が速いためです。

これにより、実際には、ラむブラリヌ関数の蚘述が終了し、受信デヌタの凊理が開始されたす。



マルチスレッドFastCGIプログラムの簡単な䟋



この䟋ではすべおが明らかになるず思いたす。唯䞀のこずは、デバッグメッセヌゞの印刷ずワヌクフロヌの「眠りに萜ちる」こずは、デモンストレヌションのみを目的ずしお行われたこずです。プログラムをコンパむルするずきは、libfcgiおよびlibpthreadラむブラリgccコンパむラオプション-lfcgiおよび-lpthreadを含めるようにしおください。



 #include <pthread.h> #include <sys/types.h> #include <stdio.h> #include "fcgi_config.h" #include "fcgiapp.h" #define THREAD_COUNT 8 #define SOCKET_PATH "127.0.0.1:9000" //    static int socketId; static void *doit(void *a) { int rc, i; FCGX_Request request; char *server_name; if(FCGX_InitRequest(&request, socketId, 0) != 0) { //     printf("Can not init request\n"); return NULL; } printf("Request is inited\n"); for(;;) { static pthread_mutex_t accept_mutex = PTHREAD_MUTEX_INITIALIZER; //    printf("Try to accept new request\n"); pthread_mutex_lock(&accept_mutex); rc = FCGX_Accept_r(&request); pthread_mutex_unlock(&accept_mutex); if(rc < 0) { //    printf("Can not accept new request\n"); break; } printf("request is accepted\n"); //   server_name = FCGX_GetParam("SERVER_NAME", request.envp); //  HTTP- (    ) FCGX_PutS("Content-type: text/html\r\n", request.out); //         FCGX_PutS("\r\n", request.out); //   ( - html- -) FCGX_PutS("<html>\r\n", request.out); FCGX_PutS("<head>\r\n", request.out); FCGX_PutS("<title>FastCGI Hello! (multi-threaded C, fcgiapp library)</title>\r\n", request.out); FCGX_PutS("</head>\r\n", request.out); FCGX_PutS("<body>\r\n", request.out); FCGX_PutS("<h1>FastCGI Hello! (multi-threaded C, fcgiapp library)</h1>\r\n", request.out); FCGX_PutS("<p>Request accepted from host <i>", request.out); FCGX_PutS(server_name ? server_name : "?", request.out); FCGX_PutS("</i></p>\r\n", request.out); FCGX_PutS("</body>\r\n", request.out); FCGX_PutS("</html>\r\n", request.out); //"" -    sleep(2); //   FCGX_Finish_r(&request); //  -  ,    .. } return NULL; } int main(void) { int i; pthread_t id[THREAD_COUNT]; //  FCGX_Init(); printf("Lib is inited\n"); //   socketId = FCGX_OpenSocket(SOCKET_PATH, 20); if(socketId < 0) { //    return 1; } printf("Socket is opened\n"); //   for(i = 0; i < THREAD_COUNT; i++) { pthread_create(&id[i], NULL, doit, NULL); } //    for(i = 0; i < THREAD_COUNT; i++) { pthread_join(id[i], NULL); } return 0; }
      
      







簡単なNginxの構成䟋



実際、蚭定の最も単玔な䟋は次のようになりたす。



 サヌバヌ{ 
	server_name localhost; 

	堎所/ { 
		fastcgi_pass 127.0.0.1:9000; 
		#fastcgi_pass unix/ tmp / fastcgi / mysocket; 
		#fastcgi_pass localhost9000; 
		 
		fastcgi_paramsを含めたす。 
	 } 
 } 




この堎合、FastCGIプログラムを正しく動䜜させるには、この構成で十分です。コメントされた行は、それぞれUnixドメむン゜ケットで動䜜し、IPアドレスの代わりにホストドメむン名を指定する䟋です。

プログラムをコンパむルしお実行し、Nginxを構成した埌、ロヌカルホストで

FastCGI HelloマルチスレッドC、fcgiappラむブラリ



最埌たで読んでくれたすべおの人に感謝したす。



All Articles