並行性の習得
並行性の習得
複数の人が同時にアプリを使用するため、アプリをできるだけ早く配信したいと考えています。したがって、並行性を処理するための何らかの方法が必要になります。恐れるな!ほとんどのWebサーバーは、デフォルトですでにこれを実行しています。ただし、スケーリングが必要な場合は、可能な限り最も効率的な方法で同時実行を使用する必要があります。
さまざまな種類の同時実行性
並行性を処理するには、マルチプロセス、マルチスレッド、イベントドリブンなど、複数の方法があります。これらのそれぞれには、それぞれの用途、長所と短所があります。この記事では、それらがどのように異なり、いつどのように使用するかを学びます。
マルチプロセス(ユニコーン)
これは、並行性を処理する最も簡単な方法です。マスタープロセスは、それ自体を複数のワーカープロセスにフォークします。ワーカープロセスが実際のリクエストを処理し、マスターがワーカーを管理します。
各ワーカープロセスのメモリには完全なコードベースがあります。これにより、このメソッドはかなりメモリを消費し、より大規模なインフラストラクチャに拡張することが困難になります。
ユースケース | あなたがおそらく知っているルビー以外の例の1つは、Chromeブラウザです。マルチプロセスの同時実行性を使用して、各タブに独自のプロセスを提供します。これにより、アプリケーション全体を停止することなく、単一のタブをクラッシュさせることができます。彼らの場合、エクスプロイトを単一のタブに分離するのにも役立ちます。 |
---|---|
実装が最も簡単です。 スレッドセーフの問題を無視します。 各ワーカーは、システムの他の部分に損傷を与えることなくクラッシュできます。 | |
各プロセスは、完全なコードベースをメモリにロードします。これにより、メモリを大量に消費します。 したがって、大量の同時接続に拡張することはできません。 |
マルチスレッド(Puma)
このスレッドモデルにより、1つのプロセスで複数のリクエストを同時に処理できます。これは、単一のプロセス内で複数のスレッドを実行することによって行われます。
マルチプロセスアプローチとは対照的に、すべてのスレッドは同じプロセス内で実行されます。これは、グローバル変数などのデータを共有することを意味します。したがって、スレッドごとに使用される追加メモリのチャンクはごくわずかです。
グローバルインタプリタロック
これにより、MRIのグローバルインタープリターロック(GIL)が表示されます。 GILは、すべてのRubyコードの実行をロックします。スレッドは並行して実行されているように見えますが、一度にアクティブになるスレッドは1つだけです。
IOはGILの外部で動作します。結果が返されるのを待ってデータベースクエリを実行すると、ロックされません。その間に、別のスレッドがいくつかの作業を行う機会があります。スレッド内のハッシュまたは配列に対して多くの計算と操作を行う場合、MRIを使用する場合は、単一のコアのみを使用します。ほとんどの場合、マシンを完全に利用するには、依然として複数のプロセスが必要です。または、GILを持たないRubiniusまたはjRubyを使用することもできます。
スレッドセーフ
複数のスレッドを使用する場合は、スレッドセーフな方法で共有データを操作するすべてのコードを注意深く記述する必要があります。これは、たとえば、ミューテックスを使用して共有データ構造を操作する前にロックすることで実行できます。これにより、データを変更している間、他のスレッドが古いデータに基づいて作業を行わないようになります。
ユースケース | これは「道路の真ん中」オプションです。短いリクエストの負荷を処理する必要がある多くの標準的なWebアプリケーション(ビジーなWebアプリケーションなど)に使用されます。 |
---|---|
マルチプロセスよりも少ないメモリを使用します。 | |
コードがスレッドセーフであることを確認する必要があります。 スレッドがクラッシュを引き起こすと、プロセスが停止する可能性があります。 GILはI/Oを除くすべての操作をロックします。 | >
イベントループ(薄い)
イベントループは、多くの同時I/O操作を実行する必要がある場合に使用されます。モデル自体は、複数のリクエストを同時に実行することを強制しませんが、多数の同時ユーザーを処理するための効率的な方法です。
以下に、Rubyで記述された非常に単純なイベントループを示します。ループはevent_queue
からイベントを取得します そしてそれを処理します。イベントがない場合は、スリープ状態になり、キューに新しいイベントがあるかどうかを確認するために繰り返します。
loop do
if event_queue.any?
handle_event(event_queue.pop)
else
sleep 0.1
end
end
イラスト版
この図では、さらに一歩進んでいます。イベントループは、OS、キュー、およびメモリを使用して美しいダンスを実行します。
ステップバイステップ
- OSはネットワークとディスクの可用性を追跡します。
- OSは、I / Oの準備ができていることを確認すると、イベントをキューに送信します。
- キューは、イベントループが最上位のイベントを取得するイベントのリストです。
- イベントループがイベントを処理します。
- 接続に関するメタデータを保存するためにメモリを使用します。
- 新しいイベントを直接イベントキューに再度送信できます。たとえば、イベントの内容に基づいてキューをシャットダウンするメッセージ。
- I / O操作を実行する場合は、特定のI/O操作に関心があることをOSに通知します。 OSはネットワークとディスクを追跡し([1]を参照)、I/Oの準備ができたらイベントを再度追加します。
ユーザーへの同時接続を多数使用する場合。 Slackのようなサービスを考えてみてください。 Chromeの通知。 | |
長所 | 接続ごとのメモリオーバーヘッドはほとんどありません。 膨大な数の並列接続に拡張できます。 |
---|---|
理解するのは難しいメンタルモデルです。 キューが蓄積しないように、バッチサイズは小さく、予測可能である必要があります。 |
どちらを使用すればよいですか?
この記事が、さまざまな並行性モデルについての理解を深めてくれることを願っています。開発者として理解するのは難しい主題の一部ですが、それを理解することで、アプリの適切なセットアップを実験して使用するためのツールが得られます。
まとめ
- ほとんどのアプリのスレッド化は理にかなっていますが、Ruby / Railsエコシステムは(ゆっくりと)このように動いているようです。
- 長時間実行されるストリームを使用して同時実行性の高いアプリを実行している場合は、イベントループを使用して拡張できます。
- 交通量の多いサイトがない場合、または従業員が休憩することを期待している場合は、古き良きマルチプロセスを選択してください。
また、スレッド内、マルチプロセスセットアップ内でイベントループを実行することができます。そうです、ストロープワッフルを食べて食べることもできます!
これらの同時実行モデルについて詳しく知りたい場合は、マルチプロセス、マルチスレッド、およびイベントループに関する詳細な記事を確認してください。
-
RubyのUNIXデーモンの理論的紹介
Unixデーモンは、バックグラウンドで実行されるプログラムです。 Nginx、Postgres、OpenSSHはいくつかの例です。彼らはいくつかの特別なトリックを使用してプロセスを「切り離し」、どの端末からも独立して実行できるようにします。 私はいつもデーモンに魅了されてきました-おそらくそれは名前です-そしてそれらがどのように機能するかを説明する投稿をするのは楽しいだろうと思いました。具体的には、Rubyでそれらを作成する方法。 ...しかし最初に。 自宅でこれを試さないでください! おそらくデーモンを作りたくないでしょう。仕事を成し遂げるもっと簡単な方法があります。 バックグラウン
-
Rubyの正規表現をマスターする
Rubyの正規表現( Ruby regex 略して)さらに処理するためにデータを抽出する目的で、文字列内の特定のパターンを見つけるのに役立ちます。 正規表現の2つの一般的な使用例には、検証と解析が含まれます。 例 : ruby正規表現を含むメールアドレスについて考えてみてください 有効な電子メールアドレスがどのように見えるかを定義できます。つまり、プログラムは有効なメールアドレスと無効なメールアドレスを区別できるようになります。 Rubyの正規表現は2つのスラッシュの間に定義されます それらを他の言語構文と区別するため。最も単純な表現は、単語または1文字にさえ一致します。 例