9

GAE サーブレットをマルチスレッド化して、同じインスタンス上の同じサーブレットが最大 10 (フロントエンド インスタンスでは、最大 # スレッドは 10 であると考えています) の異なるユーザーからの同時リクエストを同時に処理できるように、それぞれの間でタイムスライスしたいと考えています。彼ら。

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);
    }
}

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

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

4

3 に答える 3

6

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

メソッドは、doGetサーブレット コンテナーによって管理されるスレッドで実行されています。doGetリクエストが来ると、サーブレット スレッドが占有され、メソッドが戻るまで解放されません。あなたのコードでは、はオブジェクトexecutor.submitを返しFutureます。実際の結果を取得するには、オブジェクトでgetメソッドを呼び出す必要があり、タスクが完了するまでブロックされます。その後、メソッドが返され、新しいリクエストが開始されます。FutureMyTaskdoGet

私は GAE に詳しくありませんが、彼らのドキュメントによると、サーブレットをスレッドセーフとして宣言すると、コンテナーは複数の要求を各 Web サーバーに並行してディスパッチします。

<!-- in appengine-web.xml -->
<threadsafe>true</threadsafe>
于 2013-02-18T07:43:16.700 に答える
5

あなたは暗黙のうちに2つの質問をしたので、両方に答えさせてください。

1. AppEngineインスタンスで複数の同時リクエストを処理するにはどうすればよいですか?

あなたは本当に2つのことをする必要があるだけです:

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

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

したがって、基本的に、投稿したエグゼキュータコードは、各AppEngineインスタンスのサーバーコードにすでに含まれており、実際には、doGet(...)AppEngineがリクエストごとに作成(または再利用)する個別のスレッドのrunメソッド内からメソッドを呼び出します。基本的にdoGet()すでにあなたですMyTask()

ドキュメントの関連部分はここにあります(実際にはあまり言及されていませんが):https ://developers.google.com/appengine/docs/java/config/appconfig#Using_Concurrent_Requests

2.投稿されたコードはこの(または他の)目的に役立ちますか?

現在の形式のAppEngineでは、独自のスレッドを作成して使用してリクエストを受け入れることはできません。これは、前述の方法を使用してハンドラーにスレッドを作成することのみを許可しますが、この1つの要求に対して並列処理を実行することのみを許可し、2番目の要求を並列に受け入れないようにします(これは外部で発生します)。doGet(...)currentRequestThreadFactory() doGet()

名前currentRequestThreadFactory()はここでは少し誤解を招く可能性があります。current FactoryRequestThreads、つまりリクエストを処理するスレッドを返すという意味ではありません。これは、内にFactory作成できるを返すことを意味します。したがって、残念ながら、現在の実行の範囲を超えて返されたThreadFactoryを使用することは実際には許可されていません。これは、それに基づいてエグゼキュータを作成し、クラス変数に保持することで提案されているようにです。ThreadscurrentRequestdoGet()

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

appengineサーブレット内で実行できること実行できないことの詳細については、次を参照してください。

Javaサーブレット環境-サンドボックス(具体的にはスレッドセクション)

完全を期すために、コードを「合法」にする方法を見てみましょう。

以下は機能するはずですが、コードが複数のリクエストを並行して処理できるという点では違いはありません。<threadsafe>true</threadsafe>これは、appengine-web.xmlの設定によってのみ決定されます。したがって、技術的には、このコードは本当に非効率的であり、本質的に線形のプログラムフローを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()メソッドに移動するだけです。;)

余談ですが、あなたが言及した10個の同時サーブレットスレッドの制限について:

これは(一時的な)設計上の決定であり、Googleがサーバーの負荷をより簡単に制御できるようにします(具体的にはサーブレットのメモリ使用量)。

これらの問題に関する詳細については、次を参照してください。

私は超リーンサーブレットコードを強く信じているので、このトピックも私を悩ませてきました。そのため、私の通常のサーブレットは、数千とは言わないまでも数百の同時リクエストを簡単に処理できます。インスタンスあたり10スレッドというこの恣意的な制限のために、より多くのインスタンスにお金を払わなければならないのは、控えめに言っても少し面倒です。しかし、私が上に投稿したリンクを読むと、彼らはこれを認識しており、より良い解決策に取り組んでいるようです。それでは、Google I /O2013が5月にどのような発表を行うか見てみましょう...:)

于 2013-02-23T06:44:08.493 に答える
2

私はエリクソンマーカスAの評価を2番目にしています。

ただし、何らかの理由で(または他のシナリオで)、コードスニペットを開始点として使用するパスをたどりたい場合は、エグゼキュータ定義を次のように変更することをお勧めします。

private static Executor executor;

インスタンス間で静的になるようにします。

于 2013-02-26T13:42:06.430 に答える