
【目的】大量アクセスに対応できる環境を構築をするため
そもそも、MacOSのローカル環境でrails sでサーバーを起動させて、Railsアプリケーションをブラウザに表示させることができたのは、rails sでRackと呼ばれるアプリケーションサーバーが立ち上がり、Rackのアプリケーションとして、Railsアプリケーションが起動しているから。https://qiita.com/takahiro1127/items/fcb81753eaf381b4b33c
このRackアプリケーションは柔軟性があり、詳しい仕組みは分からないが、Webサーバーなしでもブラウザからのリクエストを上手くレスポンスとしてブラウザに返すことができるらしい。でも、Webサーバーのような負荷分散機能がないので、大勢の人がサイトを見ると重くなってしまうので、実運用には不向きだからRackアプリケーションとは別で、Webサーバーを立てる必要がある。
ここで、出てくるのがNginxというWebサーバー
Apatchという他のWebサーバーもあるが、Nginxはリバースプロキシ機能がついており、大量アクセスを処理するのに向いている。もともとRackアプリケーションに負荷分散機能がなく、大勢の人がサイトを見ると重くなるという問題を解決することが目的でWebサーバーを設置するわけなので、その問題をより解消してくれるサーバーとしてはNginxの方が向いていると考えられる。
【リバースプロキシ機能のイメージ】

例えば、ブラウザからのリクエストが10,000件あった時、Apacheだったら、1つのWebサーバーに10,000件のリクエスト処理を行わせるが、Nginxはプロキシサーバーがリクエストをさばき、複数のWebサーバーにリクエスト処理を分散させることができる(負荷分散)
この図で言うと、10,000件のリクエストを3つのWebサーバーにリクエストを3,333件ずつ分けて、処理を分散させることができる。他にも、Nginxの方が設定ファイルがApatchと比較しても直感的で柔軟に設定を行うことが可能になる。でも、Nginxは、CPUリソースがたくさん必要な処理には向いていない。処理時間が長くなる処理を実行した際は、そこでプロセスがブロックされてしまうので、処理能力が落ちる。
NginxはRackアプリケーションサーバーと直接リクエスト/レスポンスのやりとりをすることができないので、両者を仲介する別のアプリケーションサーバーを立てる必要がある。
それがUnicornと呼ばれるRackアプリケーション用サーバー。
Unicornはアプリケーションサーバーでありながら、Webサーバーとしても起動させることができるので、UnicornとRackアプリケーションサーバーだけでもRailsアプリケーションを動かすことはできるが、大量アクセスへの対処が苦手なので、やはり大量アクセスに対応できるWebサーバーを立てた方が良い。そういった意味でNginxと相性が良い。
Webサーバー:Nginx Rackアプリケーション用サーバー:Unicorn Rackアプリケーション:Railsアプリケーション
と上手く役割分担することができる。
また、Unicornはプロセスがmaster1つと複数個のworkerに別れていて、masterがソースコードを保持しており、実際に動くのはmasterのプロセスのコピーを持ったworkerプロセス群。なので、ソースコードを読み込む必要があるのがmasterだけであり、起動が早くデプロイ時のダウンタイムもない。また、Nginxがつなぐのはmasterのみなので、masterは各workerにロードバランサーのような形で負荷分散をしながらリクエストを送ることができる。つまり、WebサーバーであるNginxでリクエストを負荷分散させ、Rackアプリケーション用サーバーのUnicornでも負荷分散させることができるということ。
そういった意味でも大量アクセスに対応できる環境構成にすることができると考えられる。
Unicornはプロセスモデルのサーバーで、blocking I/Oモデルを採用しており、クライアントとの通信中、プロセスがある処理に専有されてしまう。もし通信完了に時間がかかる「スロークライアント」が接続されたら、続くクライアントはそのスロークライアントの通信が完了するまで完了を待たなければならない。でも、Nginxであればリバースプロキシを立てることができる。リバースプロキシを使えば、Unicornはレスポンスをリバースプロキシに届ければよくなる。またスロークライアントが通信するのもUnicornではなく、リバースプロキシになるので、スロークライアントが長時間Unicornのプロセスを専有することを防ぐことができる。
Rails5から標準搭載されているpumaはUnicornと同じくアプリケーションサーバーの一種。pumaはスレッドベースのサーバー。リスクとしてはrace condition(競合状態)を引き起こす可能性がある。なので、今回はUnicornを採用する。
※race conditionとは、並列動作する複数の存在(プロセスやスレッド)が同一のリソースへほぼ同時にアクセスしたとき、予定外の処理結果が生じる問題である
https://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/c304.html
データベース更新トランザクションの競合も、そのひとつの例である。あるトランザクションがひとつのレコードを読み取って処理し新しい値を書き戻そうとしているとき、その書き込みが終わる前に別のトランザクションが更新の目的で同じレコードからの読み出しを始めると、結果としてこのレコードには不適切な値が書き込まれることになる。