Rails は何も共有しないのでしょうか、それとも別々のリクエストが同じランタイム変数にアクセスできるのでしょうか?
-
06-07-2019 - |
質問
PHP はシェアードナッシング環境で実行されます。これは、この文脈では、すべての Web リクエストがクリーンな環境で実行されることを意味します。別の永続層 (ファイルシステム、データベースなど) を経由しない限り、別のリクエストのデータにアクセスすることはできません。
Ruby on Rails についてはどうですか?読んだばかりです ブログ投稿 別々のリクエストが同じクラス変数にアクセスする可能性があることを示しています。
おそらくこれは Web サーバーに依存するのではないかと思いました。 モングレルのよくある質問 Mongrel はリクエストごとに 1 つのスレッドを使用すると述べており、シェアードナッシング環境を示唆しています。FAQ ではさらに、RoR はスレッドセーフではないと述べており、これはさらに、新しいリクエストが前のリクエストで作成されたメモリ内オブジェクトを再利用しない限り、RoR が共有環境に存在しないことを示唆しています。
これは明らかにセキュリティに大きな影響を及ぼします。そこで 2 つの質問があります。
- RoR 環境は何も共有されませんか?
- RoR が入った場合 (または かもしれない 状況によっては実行されます)共有環境では、どの変数やその他のデータ ストレージについて心配する必要がありますか?
アップデート:さらに詳しく説明します。 Java サーブレット コンテナでは、複数のリクエストにわたって存続するオブジェクトを含めることができます。これは通常、複数のユーザーがアクセスできるデータやデータベース接続などをキャッシュするために行われます。PHP では、これをアプリケーション層で行うことはできず、Memcached などの別の永続化層で行う必要があります。したがって、二重の質問は次のとおりです。どのシナリオが RoR に似ているか (PHP または Java)、Java に似ている場合は、 どれの データ型は複数のリクエストにわたって保持されますか?
解決
簡単に言うと
- いいえ、Railsは共有なし環境で決して実行されません。
- クラス変数およびクラスインスタンス変数について偏執的になります。
長いバージョン:
Railsプロセスは、フレームワークとアプリケーションをロードすることでライフサイクルを開始します。通常、それらは単一のスレッドのみを実行し、その存続期間中に多くの要求を処理します。したがって、リクエストは厳密に順次ディスパッチされます。
それにもかかわらず、すべてのクラスはリクエスト間で持続します。つまり、クラスおよびメタクラス(クラス変数やクラスインスタンス変数など)から参照されるオブジェクトは、リクエスト全体で 共有されます。たとえば、クラスメソッドのメソッド( @var || =高価な計算
)をメモしようとすると、これが噛み付く可能性があります。現在のリクエストの間のみ持続します。実際には、計算は最初のリクエストでのみ実行されます。
表面的には、キャッシュ、またはリクエスト全体の永続性に依存する他の動作を実装するのが良いと思われるかもしれません。通常、そうではありません。これは、ほとんどの展開戦略が複数の Railsプロセスを使用して、独自のシングルスレッドの性質に対抗するためです。遅いデータベースクエリを待っている間にすべてのリクエストをブロックするのは単純ではないので、簡単な方法はより多くのプロセスを生成することです。当然、これらのプロセスは何も共有しません(おそらく、気付かないようなメモリを除いて)。クラス要素またはクラスインスタンス変数にリクエスト中にデータを保存すると、これが噛みつく可能性があります。その後、どういうわけか、ものが存在するように見えることもあれば、なくなったように見えることもあります。 (もちろん、実際には、データは一部のプロセスに存在する場合と存在しない場合があります)。
一部のデプロイメント構成(特にJRuby + Glassfish)は、実際にはマルチスレッドです 。 Railsはスレッドセーフなので、対処できます。ただし、アプリケーションはスレッドセーフではない場合があります。すべてのコントローラーインスタンスは各リクエスト後に破棄されますが、ご存知のように、クラスは共有されます。クラス変数またはクラスインスタンス変数で情報を渡すと、これが噛み付く可能性があります。同期方法を適切に使用しないと、競合状態が発生する可能性が非常に高くなります。
補足:Railsは通常、シングルスレッドプロセスで実行されます。これは、Rubyのスレッド実装がおろそかだからです。幸いなことに、Ruby 1.9では状況が少し改善されています。 JRubyの方が多く優れています。
これらのRuby実装の両方が人気を博しているため、マルチスレッドのRails展開戦略も人気と数を獲得するようです。マルチスレッドのリクエストディスパッチを念頭に置いてアプリケーションを作成することをお勧めします。
他のヒント
以下は、共有オブジェクトの変更に注意しないと何が起こるかを示す比較的単純な例です。
新しい Rails プロジェクトを作成します。
rails test
新しいファイルを作成する
lib/misc.rb
それにこれを入れます:class Misc @xxx = 'Hello' def Misc.contents() return @xxx end end
- 新しいコントローラーを作成します。
ruby script/generate controller Posts index
変化
app/views/posts/index.html.erb
このコードを含めるには:<% require 'misc'; y = Misc.contents() ; y << ' (goodbye) ' %> <pre><%= y %></pre>
(ここで、暗黙的に共有されたオブジェクトを変更します。)
- RESTful ルートを追加する
config/routes.rb
. - サーバーを起動します
ruby script/server
そしてページをロードします/posts
数回。の数が表示されます( goodbye)
連続するページがリロードされるたびに文字列が 1 つずつ増加します。
Passengerを使用した平均的な展開では、おそらくリクエスト間で(静的な)状態を維持する各プロセス内のクラスのみを共有する複数のアプリプロセスがあります。ただし、各リクエストはコントローラーの新しいインスタンスを作成します。
これは、個別の共有状態環境のクラスターと呼ぶことができます。
Javaの類推を使用するには、キャッシュを行い、リクエストごとにキャッシュを機能させることができます。すべてのリクエストで利用できるとは限りません。
共有なしは時々良い考えです。ただし、大規模なアプリケーションフレームワーク、大規模なドメインモデル、およびすべての要求で大量の構成をロードする必要がある場合ではありません。
効率化のために、Railsはメモリ内で利用可能な一部のデータを保持し、アプリケーションの有効期間中、すべてのリクエストで共有します。このデータのほとんどは読み取り専用であるため、心配する必要はありません。
アプリを作成するときは、共有オブジェクトへの書き込みを避けてください(たとえば、データベースはすぐに使用できるようになり、同時実行制御が優れています)