// BAD! (due to the blocking in Future):
implicit val defaultDispatcher = system.dispatcher
val routes: Route = post {
complete {
Future { // uses defaultDispatcher
Thread.sleep(5000) // will block on the default dispatcher,
System.currentTimeMillis().toString // starving the routing infra
}
}
}
私たちはアプリケーションを[a]ロードに公開しています。すでに多くのakka.actor.default-dispatcherスレッドが表示されています - 要求を処理しています - 小さい緑色のスニペット、そこ。
その後、我々はこれらのスレッドのブロッキング原因[B]負荷、スタート - あなたは前にアイドル状態にした後、ブロッキングに入る早期スレッド「デフォルト・ディスパッチャ-2,3,4」を参照してくださいすることができます。また、新しいスレッドは "default-dispatcher-18,19,20,21 ..."で開始されますが、すぐに眠りにつきます(!) - ここで貴重なリソースを無駄にしています!
このように起動されるスレッドの数は、デフォルトのディスパッチャの設定によって異なりますが、50を超えることはありません。私たちは2kのブロック操作を実行したばかりなので、スレッドプール全体が枯渇しています - ルーティングのインフラに他の要求を処理するためのスレッドがないようなブロック操作が支配的です。あなたのapplication.conf
のconfigureで
:
2) [good!]
ディスパッチャ行動の良い構造化されたコード/ディスパッチャ: -
(常に、以下に示すようなブロックの挙動を隔離ところでアッカのベストプラクティスである)のは、それについて何かをやってみましょうこのディスパッチャは、行動を阻止するための専用:
my-blocking-dispatcher {
type = Dispatcher
executor = "thread-pool-executor"
thread-pool-executor {
// in Akka previous to 2.4.2:
core-pool-size-min = 16
core-pool-size-max = 16
max-pool-size-min = 16
max-pool-size-max = 16
// or in Akka 2.4.2+
fixed-pool-size = 16
}
throughput = 100
}
あなたはに多くをお読みくださいのドキュメントを参照してください。しかし、主な点は、ThreadPoolExecutor
を選んだことです。このスレッドには、ブロック操作に使用できるスレッドがハード制限されています。サイズの設定は、アプリケーションの機能、およびサーバーのコア数によって異なります。
次は、デフォルトの代わりに、それを使用する必要があります。
// GOOD (due to the blocking in Future):
implicit val blockingDispatcher = system.dispatchers.lookup("my-blocking-dispatcher")
val routes: Route = post {
complete {
Future { // uses the good "blocking dispatcher" that we configured,
// instead of the default dispatcher – the blocking is isolated.
Thread.sleep(5000)
System.currentTimeMillis().toString
}
}
}
私たちは、同じ負荷、通常のリクエストの最初のビットを使用してアプリに圧力をかけた後、私たちは、ブロッキングのものを追加します。これは、スレッドプールは、この場合にはどのように動作するかです:
は通常の要求は簡単にデフォルトのディスパッチャによって処理されるので、最初は、あなたがそこにいくつかの緑の線見ることができます - それは、実際の実行だ(私はしませんよ実際にはサーバーに負荷がかかるため、ほとんどアイドルです)。
ここで、ブロック操作を開始すると、my-blocking-dispatcher-*
が起動し、設定されたスレッド数まで起動します。それはそこのすべての睡眠を扱います。また、これらのスレッドで何らかの何も起こっていなければ、それらをシャットダウンします。プールをもう一杯にしてサーバーを襲った場合、プールは新しいスレッドを開始してスリープを処理しますが、その間には "ただそこにとどまり、何もしない"。
この設定を使用すると、通常のGETリクエストのスループットは影響を受けませんでしたが、(まだかなり無料の)デフォルトのディスパッチャではまだうまく処理されていました。
これは、リアクティブアプリケーションでのあらゆる種類のブロックを処理するために推奨される方法です。アプリの動作不良部分を「隔離する」(または「隔離する」)と呼ばれることがよくあります。この場合、悪い動作はスリープ/ブロッキングです。
3) [workaround-ish]
ディスパッチャ行動blocking
が正しく適用:
をこの例では、ブロッキングOPSに直面したときに助けることができるscaladoc for scala.concurrent.blocking
メソッドを使用します。一般的には、ブロッキング操作から生き残るために、より多くのスレッドがスピンアップされます。
// OK, default dispatcher but we'll use `blocking`
implicit val dispatcher = system.dispatcher
val routes: Route = post {
complete {
Future { // uses the default dispatcher (it's a Fork-Join Pool)
blocking { // will cause much more threads to be spun-up, avoiding starvation somewhat,
// but at the cost of exploding the number of threads (which eventually
// may also lead to starvation problems, but on a different layer)
Thread.sleep(5000)
System.currentTimeMillis().toString
}
}
}
}
アプリは、次のように動作します:ああ、これはよ」のヒントを阻止するため
我々は今、公式文書の一部として、この返信が含まれていました。http://doc.akka.io/docs/akka/2.4/scala/http/handling-blocking-operations-in-akka-http-routes.html#handling -blocking-in-http-routes-scala –
上記のリンクは壊れています。 –
回答を返信してバックグラウンドで作業し続ける場合はどうすればよいですか? [この](https://gist.github.com/asarkar/37e4cb026c463f6334617e923cfc4b12)が動作しているようです。 –