2013-02-13 11 views
9

GAEサーブレットをマルチスレッド化して、同じインスタンス上の同じサーブレットが最大10個(フロントエンドインスタンスではの最大スレッド数= 10)の同時リクエスト同時に異なるユーザから、それらの各々の間でタイムスライシングを行う。 GAEの起動時マルチスレッドGAEサーブレットで同時ユーザーを処理する

public class MyServlet implements HttpServlet { 
    private Executor executor; 

    @Override 
    public void doGet(HttpServletRequest request, HttpServletResponse response) { 
     if(executor == null) { 
      ThreadFactory threadFactory = ThreadManager.currentRequestFactory(); 
      executor = Executors.newCachedThreadPoolthreadFactory); 
     } 

     MyResult result = executor.submit(new MyTask(request)); 

     writeResponseAndReturn(response, result); 
    } 
} 

だから基本的に、それはこのサーブレットにリクエストを取得する最初の時間は、Executorを作成して保存されます。新しいサーブレット要求ごとに、そのエグゼキュータを使用して新しいスレッドが生成されます。明らかに、MyTaskの中のすべてはスレッドセーフでなければなりません。

私が心配しているのは、これが本当に私が望んでいることをしているかどうかです。つまり、は、このコードで同時に複数のユーザーからの複数の要求を処理できる非ブロッキングサーブレットを作成します?そうでない場合は、なぜそれを修正するために私は何をする必要がありますか?そして、一般的に、GAEのマエストロが死んだことが間違っていることが分かる何か他のものがありますか?前もって感謝します。

+0

+1 - 私はあなたが問題について考えているやり方と、解決策にアプローチしようとした方法が本当に好きです。しかし、幸いなことにGAEはこのように動作しません。また、 'currentRequestThreadFactory()'メソッドは、その名前の読み方に基づいて期待することはできません。私はうまくいけばメソッド名のあいまいさをクリアして、以下の答えを投稿しました。 (現在のRequestThread-Factoryではなく、currentRequest-Thread-Factoryです); –

+0

好奇心から逃れてください:達成しようとしていることは何ですか?ちょっと前に戻ってきたので、まったく同じ質問に目を通していました。私は、GAEを使って何らかの長いポーリングプッシュ通知を実装することを望んでいましたが、そのようなことであなたのやり方で得られる他の多くの問題があることが判明しました。 GAEは、リクエストをスケジュールして処理する方法を調整できるという点では、かなり制限されています。そして、これらの制限のいくつかはあいまいです...しかし、それはそれがとても速くスケーラブルであることを可能にするものです... –

答えて

6

あなたのコードはうまくいかないと思います。

doGetメソッドは、サーブレットコンテナによって管理されるスレッドで実行されています。リクエストが入ると、サーブレット・スレッドが占有され、doGetメソッドが戻るまで解放されません。あなたのコードでは、executor.submitFutureオブジェクトを返します。実際の結果を得るにはメソッドをFutureオブジェクトで呼び出す必要があり、MyTaskのタスクが終了するまでブロックされます。のみ、その後、doGet方法戻り、新しい要求がで蹴ることができます。

を私はGAEに慣れていないけど、their docsによると、あなたはスレッドセーフとしてサーブレットを宣言することができ、その後、コンテナは各ウェブに複数の要求を派遣します並行して、サーバー:

<!-- in appengine-web.xml --> 
<threadsafe>true</threadsafe> 
5

あなたは暗黙のうちに二つの質問を尋ねたので、私は両方に答えてみましょう:

1.どのように私は複数の同時要求を処理するために私のAppEngineインスタンスを得ることができますか?

あなたは本当に唯一の2つのことを行う必要があります。

  1. あなたはwar\WEB-INFフォルダで見つけることができますappengine-web.xmlファイルへのステートメント<threadsafe>true</threadsafe>を追加します。
  2. すべてリクエストハンドラ内のコードが実際にスレッドセーフである、つまり、あなたのdoGet(...)にローカル変数のみを使用し、doPost(...)、などの方法や、クラスまたはグローバル変数へのすべてのアクセスを同期することを確認していることを確認してください。

このはあなたのコードは、スレッドセーフであるのAppEngineインスタンスサーバーフレームワークを教えてくれますし、あなたはそれが同時に複数の要求を処理するために、異なるスレッドでリクエストハンドラのすべてを複数回呼び出すことができるようにしていること。注:AFAIKでは、これをサーブレット単位で設定することはできません。したがって、ALLあなたのサーブレットはスレッドセーフである必要があります!

あなたが投稿した実行コードは、すでに各AppEngineインスタンスのサーバーコードに含まれており、AppEngineが作成(または再利用)する別のスレッドのrunメソッドの内部から実際にdoGet(...)メソッドを呼び出します。各リクエスト。基本的にはdoGet()MyTask()です。 https://developers.google.com/appengine/docs/java/config/appconfig#Using_Concurrent_Requests

2.このために便利な投稿のコード(または他の)目的です:(それは本当に多く言いませんが)

ドキュメントの関連部分はここですか?

AppEngineの現在のフォームでは、独自のスレッドを作成して使用してリクエストを受け入れることはできません。それだけで(これはdoGet()起こる)あなたはあなたが言及したcurrentRequestThreadFactory()メソッドを使用して、あなたdoGet(...)ハンドラ内のスレッドを作成することが、唯一の並列に2番目のを受け入れるために、この1つの要求のための並列処理を行うことなくすることができます。

currentRequestThreadFactory()の名前は、ここで少し誤解を招くことがあります。これは、currentFactoryRequestThreads、つまり要求を処理するスレッドを返すことを意味するものではありません。これは、の内部にThreadsを作成できるFactoryを返します。残念ながら、現在のdoGet()の実行範囲を超えて返されたThreadFactoryを使用することは、実際には許可されていません。たとえば、Executorを作成し、クラス変数内に保持しておくことをお勧めします。

フロントエンドインスタンスの場合、doGet()メソッドが返されたときに、doGet()コールの中で作成したスレッドはすぐに終了します。バックエンドインスタンスの場合、実行を続けるスレッドを作成することはできますが、これらのスレッド内で要求を受け入れるためにサーバーソケットを開くことはできないため、依然として自分で処理する要求を管理することはできません。

あなたががここのAppEngineサーブレット内で行うことはできませんができるかについての詳細を見つけることができます:完全性については

The Java Servlet Environment - The Sandbox(特にスレッド節)

を、見てみましょうをあなたのコードを "法的"にする方法:

次のように動作するはずですが、複数の要求を並行して処理できるコードの点で違いはありません。これはappengine-web.xmlに設定されている<threadsafe>true</threadsafe>によってのみ決定されます。つまり、技術的には、このコードは実際には効率が悪く、本質的に線形のプログラムフローを2つのスレッドに分割します。しかし、ここでそれはとにかくです:

public class MyServlet implements HttpServlet { 

    @Override 
    public void doGet(HttpServletRequest request, HttpServletResponse response) { 
     ThreadFactory threadFactory = ThreadManager.currentRequestThreadFactory(); 
     Executor executor = Executors.newCachedThreadPool(threadFactory); 

     Future<MyResult> result = executor.submit(new MyTask(request)); // Fires off request handling in a separate thread 

     writeResponse(response, result.get()); // Waits for thread to complete and builds response. After that, doGet() returns 
    } 
} 

あなたが現在処理している要求に固有のもので、別のスレッド内に既にあるので、あなたは間違いなく自分自身に「スレッド内のスレッド」を保存して単に代わりにこれを行う必要があります。

public class MyServlet implements HttpServlet { 

    @Override 
    public void doGet(HttpServletRequest request, HttpServletResponse response) { 
     writeResponse(response, new MyTask(request).call()); // Delegate request handling to MyTask object in current thread and write out returned response 
    } 
} 

さらに、MyTask.call()のコードをdoGet()メソッドに移動するだけです。これは、より簡単にGoogleがそのサーバーの負荷を制御することができます(一時的な)デザインの決定(特にメモリである

: - ;)

別に

あなたが言及した10件の同時サーブレットスレッドの制限についてサーブレットの使用)。

あなたはここで、これらの問題の詳細な議論を見つけることができます。このトピックでは、I以来、あまりにも、私のうちの一体を盗聴されています

を超希薄サーブレットコードを強く信じているので、私の通常のサーブレットは、数千ではないにしても何百という同時リクエストを簡単に処理できます。インスタンスあたり10スレッドのこの任意の制限のためにもっと多くのインスタンスを支払わなければならないということは、私にはほとんど言い難いものです。しかし、私が上に投稿したリンクを読んで、彼らはこれを認識しており、より良い解決策に取り組んでいるように思えます。それでは、月に何のアナウンスをGoogle I/O 2013をもたらすでしょう見てみましょう... :)

2

I二エリクソンの評価とマルクスA.

しかしもし、何らかの理由(またはあなたが出発点として、あなたのコードスニペットを使用してパスをフォローしたい)他のいくつかのシナリオのために、私はあなたがあなたのエグゼキュータの定義を変更することをお勧めしたい:それはインスタンス間で静的になり

private static Executor executor; 

ように。

関連する問題