web-dev-qa-db-ja.com

サーブレットはどのように機能しますか?インスタンス化、セッション、シェア変数、およびマルチスレッド

多数のサーブレットを保持するWebサーバーがあるとします。これらのサーブレット間で情報を受け渡すために、セッション変数とインスタンス変数を設定します。

2人以上のユーザーがこのサーバーにリクエストを送信した場合、セッション変数はどうなりますか?それらはすべてすべてのユーザーに共通のものですか、それともユーザーごとに異なります。それらが異なる場合、サーバーは異なるユーザーをどのように区別することができましたか?

もう1つ類似した質問です。特定のサーブレットにアクセスするn人のユーザーがいる場合、このサーブレットは最初のユーザーが初めてアクセスしたときにのみインスタンス化されるのですか。つまり、インスタンス変数はどうなりますか。

1064
Ku Jon

ServletContext

サーブレットコンテナ( Apache Tomcat など)が起動すると、すべてのWebアプリケーションをデプロイしてロードします。 Webアプリケーションがロードされると、サーブレットコンテナは ServletContext を1回作成し、サーバーのメモリに保持します。 Webアプリのweb.xmlおよび含まれるすべてのweb-fragment.xmlファイルが解析され、見つかった各<servlet><filter>および<listener>(またはそれぞれ@WebServlet@WebFilterおよび@WebListenerの注釈が付いた各クラス)がインスタンス化され、サーバーのメモリに保持されます。インスタンス化されたフィルターごとに、そのinit()メソッドが新しい FilterConfig で呼び出されます。

Servlet<servlet><load-on-startup>または@WebServlet(loadOnStartup)の値が0より大きい場合、そのinit()メソッドも起動時に新しい ServletConfig で呼び出されます。これらのサーブレットは、その値で指定された同じ順序で初期化されます(1は1番目、2は2番目など)。複数のサーブレットに同じ値が指定されている場合、それらの各サーブレットは、web.xmlweb-fragment.xml、または@WebServletクラスローディングに現れるのと同じ順序でロードされます。 「load-on-startup」値が存在しない場合、HTTP要求がそのサーブレットに初めてヒットするたびにinit()メソッドが呼び出されます。

サーブレットコンテナが上記のすべての初期化手順を完了すると、 ServletContextListener#contextInitialized() が呼び出されます。

サーブレットコンテナがシャットダウンすると、すべてのWebアプリケーションがアンロードされ、初期化されたすべてのサーブレットおよびフィルタのdestroy()メソッドが呼び出され、すべてのServletContextServletFilter、およびListenerインスタンスが破棄されます。最後に ServletContextListener#contextDestroyed() が呼び出されます。

HttpServletRequestおよびHttpServletResponse

サーブレットコンテナは、特定のポート番号でHTTP要求をリッスンするWebサーバーに接続されます(ポート8080は通常、開発中に使用され、ポート80は運用中に使用されます)。クライアント(Webブラウザーを使用するユーザー、または プログラムでURLConnectionを使用 )がHTTP要求を送信すると、サーブレットコンテナーは新しい HttpServletRequestHttpServletResponse を作成しますオブジェクトを作成し、チェーン内の定義済みのFilter、および最終的にはServletインスタンスに渡します。

filters の場合、doFilter()メソッドが呼び出されます。サーブレットコンテナのコー​​ドがchain.doFilter(request, response)を呼び出すと、要求と応答は次のフィルターに進むか、フィルターが残っていない場合はサーブレットをヒットします。

servlets の場合、service()メソッドが呼び出されます。デフォルトでは、このメソッドはdoXxx()に基づいて呼び出すrequest.getMethod()メソッドの1つを決定します。決定されたメソッドがサーブレットにない場合、HTTP 405エラーが応答で返されます。

要求オブジェクトは、URL、ヘッダー、クエリ文字列、本文など、HTTP要求に関するすべての情報へのアクセスを提供します。応答オブジェクトは、たとえば、ヘッダーと本文を設定できるようにする(通常、JSPファイルから生成されたHTMLコンテンツを使用する)ことにより、HTTP応答を制御および送信する機能を提供します。 HTTP応答がコミットされて終了すると、要求オブジェクトと応答オブジェクトの両方がリサイクルされ、再利用できるようになります。

HttpSession

クライアントが初めてwebappにアクセスするとき、および/またはrequest.getSession()を介して HttpSession を初めて取得するとき、サーブレットコンテナは新しいHttpSessionオブジェクトを作成し、長く一意のIDを生成しますsession.getId()で取得し、サーバーのメモリに保存します。サーブレットコンテナは、HTTP応答のSet-Cookieヘッダーに、名前としてCookieを、値として一意のセッションIDを指定して JSESSIONID を設定します。

HTTP Cookie仕様 (適切なWebブラウザーとWebサーバーが遵守する必要がある契約)に従って、クライアント(Webブラウザー)は、Cookieヘッダーの後続のリクエストでこのCookieを返送する必要がありますCookieが有効である限り(つまり、一意のIDは有効期限が切れていないセッションを参照している必要があり、ドメインとパスは正しい)。ブラウザーの組み込みHTTPトラフィックモニターを使用して、Cookieが有効であることを確認できます(Chrome/Firefox 23+/IE9 +でF12を押し、Net/Network タブ)。サーブレットコンテナは、Cookieという名前のCookieが存在するかどうかすべての着信HTTP要求のJSESSIONIDヘッダーをチェックし、その値(セッションID)を使用して、サーバーのメモリから関連するHttpSessionを取得します。

HttpSessionは、<session-timeout>の設定であるweb.xmlで指定されたタイムアウト値を超えてアイドル状態になるまで(つまり、リクエストで使用されなくなるまで)存続します。タイムアウト値のデフォルトは30分です。そのため、クライアントが指定された時間を超えてWebアプリにアクセスしない場合、サーブレットコンテナはセッションを破棄します。 Cookieが指定されていても、以降のすべてのリクエストは同じセッションにアクセスできなくなります。サーブレットコンテナは新しいセッションを作成します。

クライアント側では、ブラウザインスタンスが実行されている限り、セッションCookieは存続します。そのため、クライアントがブラウザーインスタンス(すべてのタブ/ウィンドウ)を閉じると、セッションはクライアント側で破棄されます。新しいブラウザインスタンスでは、セッションに関連付けられたCookieは存在しないため、送信されなくなります。これにより、まったく新しいHttpSessionが作成され、まったく新しいセッションCookieが使用されます。

手短に

  • ServletContextは、Webアプリが存続する限り存続します。これは、allセッションのall要求間で共有されます。
  • HttpSessionは、クライアントが同じブラウザーインスタンスを使用してWebアプリと対話し、サーバー側でセッションがタイムアウトしていない限り存続します。 sameセッション内のall要求間で共有されます。
  • HttpServletRequestおよびHttpServletResponseは、サーブレットがクライアントからHTTPリクエストを受信して​​から、完全な応答(Webページ)が到着するまで有効です。他の場所でnot共有されます。
  • すべてのServletFilter、およびListenerインスタンスは、Webアプリが存続する限り存続します。それらは、allセッションのall要求間で共有されます。
  • attributeServletContext、およびHttpServletRequestで定義されているHttpSessionは、問題のオブジェクトが存続する限り存続します。オブジェクト自体は、JSF、CDI、SpringなどのBean管理フレームワークの「スコープ」を表します。これらのフレームワークは、最も近い一致スコープのattributeとしてスコープBeanを格納します。

スレッドセーフティ

とはいえ、あなたの主な関心事は、おそらくスレッドセーフティです。これで、サーブレットとフィルターがすべてのリクエスト間で共有されていることがわかります。これはJavaのすばらしいところです。マルチスレッドであり、異なるスレッド(HTTP要求)が同じインスタンスを使用できます。それ以外の場合は、すべてのリクエストに対してinit()およびdestroy()を再作成するのに費用がかかりすぎます。

また、neverリクエストまたはセッションスコープのデータをinstance変数として割り当てる必要があることも認識してくださいサーブレットまたはフィルター。他のセッションの他のすべてのリクエストで共有されます。それはnotスレッドセーフです!以下の例はこれを示しています。

public class ExampleServlet extends HttpServlet {

    private Object thisIsNOTThreadSafe;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object thisIsThreadSafe;

        thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!! Shared among all requests!
        thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe.
    } 
}

こちらもご覧ください:

1760
BalusC

セッション

enter image description hereenter image description here

つまり、Webサーバーはfirst訪問時に各訪問者に一意の識別子を発行します。次回訪問者が認識されるためには、訪問者はそのIDを返さなければなりません。この識別子によって、サーバーはあるセッションが所有するオブジェクトを別のセッションのものと正しく区別することもできます。

サーブレットのインスタンス化

起動時のロード false の場合

enter image description hereenter image description here

起動時ロード true の場合

enter image description hereenter image description here

彼がサービスモードとグルーブに着くと、sameサーブレットは他のすべてのクライアントからのリクエストを処理します。

enter image description here

クライアントごとに1つのインスタンスを持つのが良い考えではないのはなぜですか?これについて考える:あなたは来たすべての注文のために1人のピザ屋を雇うのだろうか?それをすればあなたはすぐに廃業するでしょう。

それは少しリスクが伴います。覚えておいてください:この一人の男は自分のポケットの中にすべての注文情報を持っています:あなたが サーブレット上のスレッドの安全性に注意していないのなら

417
Jops

Javaサーブレットのセッションは、PHPなどの他の言語のセッションと同じです。ユーザーに固有です。サーバーは、Cookie、URL書き換えなど、さまざまな方法で追跡できます。この Java doc の記事では、Javaサーブレットのコンテキストで説明し、セッションの正確な方法を示しています。維持されるのは、サーバーの設計者に残された実装の詳細です。この仕様では、サーバーへの複数の接続にわたってユーザーに固有のものとして維持する必要があることのみを規定しています。両方の質問の詳細については、 Oracleのこの記事 をご覧ください。

Editサーブレット内部でセッションを操作する方法に関する優れたチュートリアル ここ があります。 here は、Javaサーブレット、その概要、および使用方法に関するSunの章です。これら2つの記事の間に、すべての質問に答えることができるはずです。

42
Chris Thompson

サーブレットコンテナ(Apache Tomcatなど)が起動すると、何か問題が発生したりコンテナサイドのコンソールにエラーが表示されたりすると、web.xmlファイルから(アプリケーションごとに1つだけ)読み込まれます。 web.xmlを使用してアプリケーションを展開する(デプロイメント記述子と呼ばれる)。

サーブレットのインスタンス化フェーズでは、サーブレットインスタンスは準備ができていますが、2つの情報が欠落しているため、クライアント要求を処理できません。
1:コンテキスト情報
2:初期設定情報

サーブレットエンジンは、上記の不足している情報をカプセル化したservletConfigインタフェースオブジェクトを作成します。サーブレットエンジンは、servletConfigオブジェクト参照を引数として指定することによって、サーブレットのinit()を呼び出します。 init()が完全に実行されると、サーブレットはクライアントの要求を処理する準備が整います。

Q)サーブレットの有効期間中に、インスタンス化と初期化が何回行われるのですか。

A)1回だけ(すべてのクライアント要求に対して新しいスレッドが作成される)、サーブレットの1つのインスタンスだけが、任意の数のクライアント要求を処理します。つまり、1つのクライアント要求サーバーを処理した後は死にません。他のクライアントの要求を待ちます。つまり、サーブレットを使って(内部的にサーブレットエンジンがスレッドを作成する)どのCGI(すべてのクライアント要求に対して)の制限が克服されるのか。

Q)セッションコンセプトはどのように機能しますか?

A)getSession()がHttpServletRequestオブジェクトで呼び出されるたびに

ステップ1 :リクエストオブジェクトは着信セッションIDについて評価されます。

ステップ2 :IDが利用できない場合は、まったく新しいHttpSessionオブジェクトが作成され、対応するセッションIDが(つまりHashTableの)生成されます。セッションIDはhttpservletレスポンスオブジェクトに格納され、HttpSessionオブジェクトの参照はサーブレットに返されます。 doGet/doPost).

ステップ3 :ID利用可能な真新しいセッションオブジェクトが作成されていない場合、セッションIDをキーとしてセッションのコレクション内で検索が行われ、リクエストオブジェクトから検索されます。

検索が成功すると、セッションIDがHttpServletResponseに格納され、既存のセッションオブジェクト参照がUserDefineservletのdoGet()またはdoPost()に返されます。

注意:

1)サーブレットコードからクライアントに制御が移るとき、セッションオブジェクトがサーブレットコンテナ、すなわちサーブレットエンジンによって保持されていることを忘れないでください。

2)マルチスレッドは、サーブレット開発者が実装するために残しています。つまり、クライアントからの複数の要求を処理しても、マルチスレッドコードに煩わされることはありません。

ショートフォーム:

サーブレットは、アプリケーションの起動時(サーブレットコンテナにデプロイされているとき)、またはサーブレットのインスタンス化時に最初にアクセスされたとき(起動時の設定に応じて)に作成され、サーブレットのinit()メソッドが呼び出されます。その後、サーブレット(その唯一のインスタンス)がすべての要求を処理します(そのservice()メソッドは複数のスレッドによって呼び出されます)。そのため同期をとることはお勧めできません。アプリケーションがアンデプロイされた(サーブレットコンテナが停止した)ときは、destroy()メソッドが呼び出されるので、サーブレットのインスタンス変数を避ける必要があります。

33
Ajay Takur

セッション - クリストンプソンが言ったこと。

インスタンス化 - コンテナがサーブレットにマップされた最初のリクエストを受け取ると、サーブレットはインスタンス化されます(起動時に<load-on-startup>web.xml要素を使用してロードするようにサーブレットが設定されている場合を除く)。同じインスタンスが後続の要求を処理するために使用されます。

20
Lauri Lehtinen

サーブレット仕様 JSR-315 は、サービス(およびdoGet、doPost、doPutなど)メソッドにおけるWebコンテナの振る舞いを明確に定義しています(2.3.3.1マルチスレッドの問題、9ページ)。

サーブレットコンテナは、サーブレットのサービスメソッドを通じて同時リクエストを送信することができます。リクエストを処理するために、サーブレット開発者はサービスメソッド内の複数のスレッドとの同時処理に対して適切な準備をしなければなりません。

推奨されていませんが、開発者にとっての代替案は、サービスメソッドに一度に1つのリクエストスレッドしかないことをコンテナに保証するように要求するSingleThreadModelインターフェースを実装することです。サーブレットコンテナは、サーブレット上の要求をシリアル化することによって、またはサーブレットインスタンスのプールを維持することによって、この要件を満たすことができます。サーブレットが配布可能としてマークされているWebアプリケーションの一部である場合、コンテナはアプリケーションが配布されている各JVMにサーブレットインスタンスのプールを保持することがあります。

SingleThreadModelインタフェースを実装していないサーブレットの場合、サービスメソッド(またはHttpServlet抽象クラスのサービスメソッドにディスパッチされるdoGetやdoPostなどのメソッド)がsynchronizedキーワードで定義されている場合、サーブレットコンテナはインスタンスプールアプローチを使用できません。しかし、それを通して要求を直列化しなければなりません。パフォーマンスへの悪影響のため、開発者はこれらの状況でサービスメソッド(またはそれにディスパッチされたメソッド)を同期しないことを強くお勧めします。

13
tharindu_DG

上記の説明から明らかなように、 SingleThreadModelサーブレットは、サーブレットコンテナによってスレッドセーフになります。コンテナ実装はこれを2つの方法で行うことができます。

1)単一インスタンスへの要求の直列化(キューイング) - これは、service/doXXXメソッドを同期化するSingleThreadModel BUTを実装していないサーブレットに似ています。または

2)インスタンスのプールを作成する - これは、サーブレットをホストする環境の制限的なパラメータ(メモリ/ CPU時間)に対する、より良いオプションであり、サーブレットの起動/初期化の努力/時間の間のトレードオフです。