Q.クローラー修正時にunicornの再起動が必要ない理由を答えよ
Answer
クローラーのロジックとアプリケーションサーバーであるunicornは独立しているため。
執筆動機
タイトルの疑問は昨年の秋頃から持ち始めたものの、
色々なことを言い訳にして特に調べずにいました。
しかしながら、時間が過ぎるにつれて自分がリリース時期を決定したり、
自力で保守運用をする必要が出てきたため、自分で調べることにしました。
unicornとは何か?
Answerのところでunicornがアプリケーションサーバーであると書きました。
アプリケーションサーバーはWebアプリケーションを支える技術の1つである、
Web3層構造の1要素です。
Web3層構造
- Webサーバー:クライアントからリクエストを受け取り、Webアプリケーション処理を渡す(例: apache/nginx...etc)
- アプリケーションサーバー:Webアプリケーションの処理を行う(例: unicorn/puma/passenger/gunicorn...etc)
- DBサーバー:アプリケーションサーバーからのデータの保存や更新、削除をする
何故unicornが必要になるのか?
ローカルの開発環境で開発する際にはunicornは必要ありませんが、
本番環境では必要になります。
Webアプリケーションの構成は開発と本番で次のように異なっています。
(Real World HTTP P.353,356の図を基に作成)
この理由はリクエストの数です。
開発環境と異なり、本番環境では複数のリクエストが同時に来ることが想定されます。
このため、ブラウザのリクエストを受けるWebサーバーを配置し、
WebアプリケーションはそれらのWebサーバーの裏で動作させます。
なお、RackとはWebアプリケーションサーバーとWebアプリケーションフレームワーク間の
インターフェースを共通化した仕様であり実装となっているライブラリです。
前段にワーカープロセスを起動するWebサーバーを置いて、
その裏側をRackで接続し、パフォーマンスを上げる構成がよく取られます。
unicornはどのように動作するのか
unicornがどのように動作するのかを
Railsで開発したWebアプリケーションを起動させる例で考えます。
1.プロセスの生成:
unicornを起動するとマスタープロセスが生成されます。
2.初期化コードの実行:
マスタープロセスが起動されると、Railsはその初期化コードを実行します。
3.コードの読み込み:
2の過程で、必要なライブラリやGem、アプリケーションのコードがメモリ上に読み込まれます。
これにより、リクエストの処理中に毎回ディスクからコードを読み込む必要がなくなり、応答速度が向上します。
4.ワーカープロセス/スレッドの生成:
1で生成されたプロセスをフォークして複数のワーカー(子)プロセスが生成されます。
この時点で前回起動時のRailsアプリケーションの状態(メモリ上のコードやデータ)はすべて破棄され、新しくロードされたコードや設定が有効になります。
この段階で回答に必要な知識が揃いました。
unicornが初期化される際に、アプリケーションコードが読み込まれてクライアントからのリクエストを受け付ける準備が整います。
したがってコードを変更しただけではリクエストで返却される内容は変わらず、
unicornを再起動して初期化を行う必要があります。
クローラー動作時の挙動
回答は出ましたが、クローラーの動作時にはどのような手順を踏むのかが
気になったので調べてみました。
クローラーをcronで定期実行することを想定しています。
1.cronの起動:
システムが起動すると、cronデーモンも自動的に起動します。
cronデーモンは、指定された時間にジョブを実行するためのバックグラウンドプロセスです。
2.スケジュールの確認:
cronデーモンは、毎分crontabを確認してジョブの有無をチェックし、
指定された時間にジョブを実行するためのスケジュールを決定します。
3.処理の起動:
ジョブの実行時刻が来ると、ジョブをキューに追加されて実行されます。
終わりに
調べる過程でunicornだけではなく、linuxなど他の分野の知識が不十分であることがよくわかりました。
今回は本やネットの記事を断片的にまとめるだけになってしまいましたが、Railsなどのソースコードを理解することやサーバーでの動作を確認することが必要であることを認識できました。
補題①:unicornとpumaの違い
unicorn:
マスタープロセスが複数のワーカープロセスを生成(フォーク)しますが、
各プロセス内では複数のスレッドを作りません。
puma:
マスタープロセスが子プロセス(ワーカー)をフォークしますが、
各プロセス内に複数のスレッドを作成することができます。
pumaの方がメモリを効率的に使うことができます。
また、複数のリクエストを同時に処理することができるので、遅いリクエストがあっても同時に処理することができます。
補題②:プロセスとスレッドの違い
プロセス: OSで実行中のプログラム。プロセス内には1つ以上のスレッドが含まれる。
スレッド: CPUから見たプログラムの実行単位。
複数のプロセス同士は同じメモリ領域を共有できないが、
同じプロセス内のスレッド同士は、同じメモリ領域を共有することができるため、
プロセス内に複数のスレッドを作成することで、メモリの利用効率を高めることができます。
ただし、複数のスレッドが同じメモリ領域を共有しているため、
誤って別のスレッドに影響する情報を書き換えてしまう可能性があります。
(スレッドセーフでない状態)
参考文献・記事
Real World HTTP 第2版
パーフェクト Ruby on Rails 【増補改訂版】
Linuxシステム管理標準教科書
Pumaの本当の力を引き出す
Railsのアプリケーションサーバーのプロセス数とスレッド数の設定方法
cron の意外な落とし穴!
Discussion