2012-02-16 3 views
2

私は、JavaサーブレットにHTTPリクエスト(テスト目的でGET)をとる設定を行っています。どのように動作するのですか、serはletがブラウザからリクエストを受け取り、それを解析し、TCPソケットを介して 'メイン'サーバーに送信します。このサーバーは要求を処理し、応答を返します。サーブレットは、以前にConcurrentHashMapに格納されていたHttpServletResponseを取得し、PrintWriterを開き、応答を返します。 HttpServletResponseがPrintWriterに書き込まれた情報を返すとは限りませんが、すべてがスムーズに進んでいます。ブラウザは毎回「OK」の応答を受信しますが、レスポンスには書き込みしようとする情報は含まれていないことがよくあります。HttpServletResponseが定期的に途中で送信されているようです

以下は、HttpServletResponseインスタンスを手渡した最初のdoGetのコードと、レスポンスバッファに書き込むメソッドです。それ以降はブラウザが受け取ったレスポンスが含まれます。その後、問題を確実に解決するために、期待される結果を確実に得る方法についてのいくつかの観察。

唯一の変数は、応答が書き込まれるかどうかであるように見えることに注意してください。私は、出力ログを注いだし、応答が期待どおりに書き込まれた時間とそうでない時間との間に他の違いは見つかりません。私が書いたHTTPServletResponseListenerは毎回レスポンスを受け取ります。

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

String js = request.getParameter("json"); 
// Omitting try-catch for space 
Message msg = this.parser.parseToMessage(js); 
this.sc.send(msg, new HTTPServletResponseListener(response)); 
} 

および応答で呼び出されHTTPServletResponseListener法[Glassfishの3.1.1を使用して]

(のみローカルフィールドにローカルHttpServletResponseのを割り当てるコンストラクタ以外に、これが唯一の方法である)

public void handleResponse(ResponseMessage response) { 
    DataParser parser = new JSONParser(); 
    String temp = parser.parseToString(response); 
    httpResponse.setContentType("application/json"); 
    httpResponse.addHeader("Hmm","yup"); 
    try { 
    PrintWriter out = httpResponse.getWriter(); 
    out.println(temp); 
    } catch (IOException ex) { 
     Logger.getLogger(HTTPServletReponseListener.class.getName()).log(Level.SEVERE,null,ex);   
    } 
} 

応答とブラウザがそれを受信します。

意図したとおりにそれが動作するとき:

応答が空である:意図したとおりの応答がある場合は

HTTP/1.1 200 OK 
X-Powered-By: Servlet/3.0 JSP/2.2 (GlassFish Server Open Source Edition 3.1.1 Java/Sun   Microsystems Inc./1.6) 
Server: GlassFish Server Open Source Edition 3.1.1 
Content-Length: 0 
Date: Thu, 16 Feb 2012 15:26:35 GMT 

HTTP/1.1 200 OK 
Hmm: yup 
X-Powered-By: Servlet/3.0 JSP/2.2 (GlassFish Server Open Source Edition 3.1.1 Java/Sun Microsystems Inc./1.6) 
Server: GlassFish Server Open Source Edition 3.1.1 
Content-Type: application/json;charset=ISO-8859-1 
Content-Length: 126 
Date: Thu, 16 Feb 2012 15:27:30 GMT 

観察:

私は、以下の手順を実行して、応答の時間の95%を取得することができます....そうでなければ、約50%の成功率。

ブラウザ上でヒットリフレッシュ...(別のテスト要求を送信する)、再びリフレッシュを打つ前に、より頻繁に 待ち少なくとも5秒で作業する傾向が展開後の最初のカップルを要求 が、これは確実に動作する傾向があります。失敗した場合、15秒待たなければならなくなり、その後再び動作する傾向があります。

応答を待つ前にHttpServletResponseが書き込まれている場合は、毎回動作します。

この度は読んでいただきありがとうございます。私は他のStackoverflowの質問を読んだことがありますが、私は接続がない限り、この特定の問題に触れるものはありません。

+1

'sc.send()'コールは非ブロック化ですか?そうであれば、サーブレットコンテナがリクエスト/レスポンスオブジェクト(ほとんどの場合)をプールすると問題に陥る可能性があります。 –

+0

それは確かにノンブロッキングです、私はあなたと以下の@tomaszが死んでいると思います、私はこれで仕事に戻ることができるようになるとすぐに更新します:) –

答えて

3

これは最も興味深いラインです:

this.sc.send(msg, new HTTPServletResponseListener(response)); 

私はscを呼び出していると、あなたはまた、応答が到着したときに通知されるリスナーを渡している。この外部TCPサーバである疑いがあります。ここで重要な前提は:私は正しく、TCPサーバーが非同期にの応答を送信し、の別のスレッドでリスナーに通知しています

これが該当する場合、その動作について説明します。 3.0より前のサーブレットでは、リクエスト全体をdoGet内で処理する必要がありました。コードがdoGet()のままになると、サーブレットコンテナは要求全体が処理されたものとみなし、要求を破棄します。

競合状態を導入しました。doGet()は、出力ストリームに何も書き込まずに返します。コンテナがレスポンスを処理して返信するまでには、数ミリ秒かかります。この短い時間の間に、外部TCPサーバーがデータを返し、リスナーに通知すると、データは通過します。しかし、サーバーの速度が少し遅い場合は、すでに処理されている接続に応答を送信しています。

このように考えてみましょう:ブラウザが電話をかけ、doGet()が呼び出され、バックエンドサーバーが呼び出されます。 doGet()リターンとサーブレットコンテナは、完了したものとみなします。これは返信(空の)応答を返し、この要求について忘れてしまいます。ミリ秒またはほんの数秒後に応答がバックエンドサーバーから戻ってきます。しかし、接続がなくなり、すでに送信され、ソケットが閉じられ、ブラウザが応答をレンダリングしました。あなたはあなたの容器を言っていません:ちょっと、待って、私はその応答を完了していない!

ソリューションへの最高の最悪から

  1. は積極doGet()のレスポンスのため/投票を待ちます。

  2. 外部TCPサーバーコールをブロックします。 ResponseMessageを返し、リスナーコードをdoGet()に戻すように、TCPサーバーファサードを変更します。例:

    ResponseMessage responseMsg = this.sc.send(msg); 
    DataParser parser = new JSONParser(); 
    String temp = parser.parseToString(responseMsg); 
    httpResponse.setContentType("application/json"); 
    httpResponse.addHeader("Hmm","yup"); 
    PrintWriter out = httpResponse.getWriter(); 
    out.println(temp); 
    
  3. 使用サーブレット3.0非同期サポート、それはあなたのユースケースにはより良い選択であり、変更の範囲は非常に制限されます。 doGet()

    final AsyncContext asyncContext = request.startAsync(request, response); 
    

    HTTPServletResponseListenerで設定が完了したら:

    asyncContext.complete(); 
    

    startAsync()への余分な呼び出しがコンテナに指示します:私は、doGet()から戻ってきたにもかかわらず、この要求で終了していません。を保持してください。私はしばらく前にServlet 3.0についてarticleを書きました。

+0

うーん、あなたは頭の爪に当たったと思います。私は今夜​​あなたが提供した非同期ソリューションを試す機会を得るまで、これをマークしておきますが、あなたの説明は私が見ているものに完全に合っています。レスポンスありがとう! –

+0

@DaveH: 'async'サポートは、HTTPスレッドをブロックしないので、はるかに優れたスケーラビリティとクリーンなコードを提供するため、特に魅力的です。これが問題かどうか聞いて待つことはできません。 –

+1

非同期ソリューションを試してみると、それは魅力のように機能します!徹底的な対応をお寄せいただき、ありがとうございました。 –

関連する問題