LoginSignup
4
4

More than 5 years have passed since last update.

Shutdown Patterns in Akka 2 和訳

Posted at

原文: Shutdown Patterns in Akka 2
意訳が多く混じります. 誤訳も多く混じっているかもしれません.

原文にある図はこの記事にはまだありません. 今年中に作ってアップロードしたい...

Shutdown Patterns in Akka 2

I’ve seen a question pop up a number of times on the Akka Mailing List that looks something like: “How do you tell Akka to shut down the ActorSystem when everything’s finished?” It turns out that there’s no magical flag for this, no configuration setting, no special callback you can register for, and neither will the illustrious shutdown fairy grace your application with her glorious presence at that perfect moment. She’s just plain mean.

「すべての仕事が終わった後にActorSystemをシャットダウンするにはどうすればよいですか?」この手の質問はAkkaのメーリングリストで幾度となく目にしてきました. 結論から言うと, これを達成するためのフラグや設定項目, コールバックなどは存在しません. 完璧なタイミングで止めてくれるシャットダウンの妖精は存在しないのです.

In this post, we’ll discuss why this is the case and provide you with a simple option for shutting down “at the right time”, as well as a not-so-simple-option for doing the exact same thing.

本記事ではなぜこれらが存在しないかについてと, 正しくシャットダウンするための簡単な方法, および少し複雑な方法について述べます.

What the Heck Does “Finished” Mean?

Before we talk about how to shut down when the app is finished, we have to first describe what it means to say the app is “finished” at all. The most natural answer to this question appears to be, “When all the Mailboxes are empty.” Natural, yes; correct, no. :)

アプリケーションの終了時にシャットダウンする方法を議論する前に, そもそも「アプリケーションの終了」とは何なのでしょうか. この疑問に対するありがちな答えは「Mailboxが空になったとき」です. これは一見正しそうに見えますが, 実は間違っています.

The obvious reason for this lack of correctness is due to the possibility that there could be a person out there who hasn’t sent their message yet. The app isn’t finished because it hasn’t seen all of it’s work.

これが間違っている明白な理由は, Mailboxが空であっても, 誰かがまだそこにメッセージを送ろうとしている可能性があることです. その誰かの仕事が終わっていないのですから, アプリケーションの終了とは言えません.

But there’s something more subtle here. Actor Mailboxes can be empty while they are still chugging away doing “stuff”. Have a look…

もう少し微妙な例を挙げましょう. Actorが何か仕事をしている途中にもMailboxは空になり得るのです. 下図を見てください:

<図はまだない>

The probability of this occuring while you have hundreds of Actors chugging away is pretty low, but that probability increases as the app winds down. Even if the odds were 0.004%, it wouldn’t matter - 0.004% is still a pretty questionable platform on which to design an algorithm.

あなたが数百ものActorを扱ったとしてもこのようなことが起こるケースは極めて低いと言えるでしょう. しかし, これが起きる可能性はアプリケーションの稼働時間に伴い増加していきます. もしこれが起きる確率が0.004%だったとしても, 果たしてそのような環境下でアルゴリズムを運用することが適切であるかは極めて疑わしいでしょう.

“Ah, but Akka can know if the Actor is working, so don’t shut down while there are non-empty Mailboxes and Actors are working!” you may say. Sure, but what if the Actor spawns work off to a Future? Or it has a request out to another machine waiting on a response? You wouldn’t want to shut down in these cases. On the flip-side, if there is a simple busy-wait ticker somewhere that keeps the app “busy” even though it’s not, would you want to stay running forever? Probably not.

「でも, AkkaはActorが仕事をしているかどうかを知ることができるのだから, Mailboxが空でないかとActorが仕事をしているかで終了のタイミングを判断できるのでは?」と思うかもしれません. では, Futureに対して仕事をするActorならばどうでしょう? あるいはActorが外部のマシンにリクエストを投げて応答を待っていたとしたら? どちらのケースもシャットダウンすべきではありません. また逆に, 単純なビジーウェイトのように定期的なメッセージが発生するケースで, アプリケーションが常に仕事中と認識されて永遠に止まらなくなるのは良いのでしょうか? これはおそらく貴方の望むこととは異なるでしょう.

The bottom line is that an app is “finished” when the app says it’s finished, not when Akka guesses that it’s finished. What we’re going to cover are mechanisms by which you can tell Akka that you’re done, but it’s going to depend on what it is that your app is doing.

結論として, アプリケーションの終了とはアプリケーション自身によって定義されるものであり, Akkaはアプリケーションの終了を推測することはできません. これから紹介するのは, あなたがAkkaに終了を告げるための仕組みですが, これらはあなたのアプリケーションが何をしたいかに依存します.

The DABOWoTeD Pattern

A common idiom is the Do A Bunch Of Work Then Die pattern. It’s pronounced just like you’d think… It’s this type of program that people seem most concerned with, when it comes to shutting down after completion. After all, apps that are supposed to run 24/7 generally aren’t concerned with this, since they have to handle hardware failures more often than shutdowns.

よく知られているのはDo A Bunch Of Work Then Die pattern (まとまった仕事をしてから死ぬパターン) です. あなたの思った通りの発音で正しいでしょう (訳注: "devote/身を捧げる"に掛けたもの? 原文に関連するwebページ以外で "DABOWoTeD" という単語を使用している文書は見つからなかった). これは多くの人が最も関心を持っているであろう, 仕事が終わった後にシャットダウンするタイプのプログラムです. これは24時間365日実行されるアプリケーションではあまり関係がないことかもしれません. そういったケースではシャットダウンの方法は問題とならず, ハードウェア障害への対処が主たる課題となるためです.

The idea with DABOWoTeD is that you’ve created a bunch of Actors to do some work, and when they’re “done” that work, it’s time to shut the system down. This is also possible with Futures, and we’ll cover those first.

DABOWoTeDの考え方は, 一度に多くのActorを生成し, それらが完了したときシステムをシャットダウンするというものです. はじめに, Futureによって達成可能であることを示します.

Futures

Futures are easy because there’s a really simple way to know when everything’s done - use a sequence. Essentially, you’re just doing a big fork-join, and when everything joins up, shut the system down. It looks something like this:

Futureを使う方法は簡単です. すべての仕事が終わったことを知るための実にシンプルな方法, それはsequenceを使うことです. 本質的には大きなfork/joinを行い, すべての仕事がjoinされたらシステムをシャットダウンするだけです. これは次のようになります:

FutureShutdown.scala
// Spawn your futures
val fs = (1 to 100).map { i =>
  Future { Thread.sleep(i); i }
}

// Wrap all of the work up into a single
// Future
val f = Future.sequence(fs)

// Wait on it forever - i.e. until it's done
Await.result(f, Duration.Inf)

// Shut down
system.shutdown() 

It just doesn’t get much simpler than that, so if you’ve got a fork-join execution, just be awesome, use that pattern and collect your profit.

これ以上シンプルな方法はありません. これで用途を満たすならば, これが最善でしょう.

Actors

Actors are more interesting here because they are generally employed when the application is more complex. It’s not so clear what “finished” means when algorithms with Actors are involved because of that higher level of complexity.

Actorはアプリケーションがより複雑な場合に使用されるため, 面白くなります. Actorのアルゴリズムがアプリケーションの終了に絡んでくると複雑性が増し, 終了が何を意味するかが分かりづらくなってきます.

We’re going to make a very reasonable assumption here: at least one Actor knows when things are “finished”. With this simple assumption, we have enough information to create our shutdown hook, which is going to be related to the Terminator that I described a short while ago.

ここでは1つ仮定を置きます. 少なくとも1つのActorがアプリケーションの終了を判断できることです. この仮定により, 我々はシャットダウンフックを作るための情報を得たことになります. これは前に述べたTerminator (訳注: 別記事) に関係します.

The Reaper

The Reaper is an Actor that is in charge of collecting dead souls. He has been told to watch over a number of Actors, waiting for them die. When he sees the last one give up its ghost he performs some action, and that action will be, in this case, to shut down the ActorSystem. Never fear the reaper.

Reaperは死者のソウルを回収する役割を担う死神です. 彼は数多くのActorを監視し, その死を待ちます. 彼は最後のActorが死ぬのを見届けると何らかのアクションを実行します. 今回のケースでは, ActorSystem全体のシャットダウンが適切な任務と言えるでしょう. 死神を恐れないでください.

<図はまだない>

In the above diagram, we see that the Reaper is just another Actor, and he has been told to watch the green nodes, B, C, D and H. No single one of these key Actors denotes the completion of the application, but when all of them are finished, then we know that the application is done. They signal their completion by dying, and when the Reaper collects all those souls, he’s going to shut down the system.

上図では, ReaperもまたひとつのActorであり, B・C・D・Hの緑のノードを監視していることが分かります. これらのKey Actorすべてが終了したとき, アプリケーションの終了を意味します. これらのActorは終了を自身の死によって表現し, Reaperは彼らのソウルをすべて回収した後でシステム全体をシャットダウンします.

Reaper Code

The Reaper itself is wonderfully simple. All it has to do is accept a message that tells it to watch an ActorRef, and then make a call to a method when everything it’s watching has kicked the bucket.

Reaper自体は非常にシンプルです. やるべきことメッセージを受け取ってはActorRefの監視を開始し, 監視しているすべてのActorが居なくなったときにメソッドを呼ぶだけです.

Reaper.scala
import akka.actor.{Actor, ActorRef, Terminated}
import scala.collection.mutable.ArrayBuffer

object Reaper {
  // Used by others to register an Actor for watching
  case class WatchMe(ref: ActorRef)
}

abstract class Reaper extends Actor {
  import Reaper._

  // Keep track of what we're watching
  val watched = ArrayBuffer.empty[ActorRef]

  // Derivations need to implement this method.  It's the
  // hook that's called when everything's dead
  def allSoulsReaped(): Unit

  // Watch and check for termination
  final def receive = {
    case WatchMe(ref) =>
      context.watch(ref)
      watched += ref
    case Terminated(ref) =>
      watched -= ref
      if (watched.isEmpty) allSoulsReaped()
  }
}

A “Production” Reaper

Given the simplicity of the Reaper, we can now create an even simpler Production Reaper, that shuts down the ActorSystem when everything’s 6 feet in the ground.

Reaperを使うとProduction Reaperも簡単に書けます. すべての監視対象が死んだ後にActorSystemをシャットダウンします.

ProductionReaper.scala
class ProductionReaper extends Reaper {
  // Shutdown
  def allSoulsReaped(): Unit = context.system.shutdown()
}

Testing the Reaper

It’s always nice to have tests. Here we use Akka’s TestKit along with its TestProbe and ImplicitSender to hook up everything we need to test our Reaper.

いついかなるときもテストを書くのは良いことです. ここではAkkaのTestKitTestProbe, ImplicitSenderを使ってReaperのテストに必要なものをフックしてみます.

TestReaper.scala
import akka.actor.{ActorSystem, Props, ActorRef}
import akka.testkit.{TestKit, ImplicitSender, TestProbe}
import org.scalatest.{WordSpec, BeforeAndAfterAll}
import org.scalatest.matchers.MustMatchers

// Our test reaper.  Sends the snooper a message when all
// the souls have been reaped
class TestReaper(snooper: ActorRef) extends Reaper {
  def allSoulsReaped(): Unit = snooper ! "Dead"
}

class ReaperSpec extends TestKit(ActorSystem("ReaperSpec"))
       with ImplicitSender
       with WordSpec
       with BeforeAndAfterAll
       with MustMatchers {
  import Reaper._

  override def afterAll() {
    system.shutdown()
  }

  "Reaper" should {
    "work" in {
      // Set up some dummy Actors
      val a = TestProbe()
      val b = TestProbe()
      val c = TestProbe()
      val d = TestProbe()

      // Build our reaper
      val reaper = system.actorOf(Props(new TestReaper(testActor)))

      // Watch a couple 
      reaper ! WatchMe(a.ref)
      reaper ! WatchMe(d.ref)

      // Stop them
      system.stop(a.ref)
      system.stop(d.ref)

      // Make sure we've been called
      expectMsg("Dead")
    }
  }
}

Application with PoisonPill

The Reaper is very good at being flexible when the goal is to drain a set of (one or more) Mailboxes. Let’s say you have a message processing system whereby you send a bunch of messages (or work) to some Actors, which may send more messages (or work) to other Actors. What you want to do is shut down when a key set of Mailboxes have been drained. For this, we use Akka’s PoisonPill message. The PoisonPill goes into the Actor’s Mailbox and will get processed in due course, which will kill the Actor. The key point is that all of the messages that exist ahead of the PoisonPill will be processed. This is different than just shutting down an Actor using system.stop.

1つ以上のMailboxを空にすることが目的であるとき, Reaperは柔軟性の点で非常に優れています. メッセージング機構を利用し, 別のActorにメッセージを送る可能性があるActorに対してまとまったメッセージを送ることを考えてみましょう. あなたの目的はKey Mailboxが空になったときにシャットダウンすることです. このケースではAkkaのPoisonPill(毒薬)メッセージが適しています. PoisonPillがMailboxに入って処理されるとActorは死にます. ポイントは, PoisonPill以前のメッセージはすべて処理されることです. これがActorがsystem.stopを使うこととの相違点です.

We can see how PoisonPill messages can be propagated through an Actor application in the following:

下図にPoisonPillがアプリケーションのActorにどのように伝播するかを示します:

<図はまだない>

Once the last PoisonPill message has been processed, the Reaper will kick in and do whatever it is you want done, such as shut down the ActorSystem.

最後のPoisonPillメッセージが処理されたときReaperが作動し, ActorSystemのシャットダウンなどの, あなたの望む挙動を行います.

Conclusion

Akka doesn’t know when your'e done doing what it is you’re doing since only you know what it is that you’re doing. Here we’ve covered a couple of mechanisms you can employ for knowing when to shut down your system. There are, of course, many others that you can probably think of, including something as simple as an external message that tells a specific Actor to ShutDown.

あなたがしたいことはあなただけが知っており, Akkaにはあなたが何をしたいのか分かりません. 本記事はシステムをシャットダウンするための仕組みを2つ紹介しました. これ以外にも, 特定のActorだけをシャットダウンするメッセージを送るなど, 考え得る仕組みは無数にあります.

With the Reaper pattern we’ve made use of the Actor paradigm to put a level of abstraction into the system, which uses the basic functionality of Actors (namely that of DeathWatch) to add “system level” behaviour to our applications. The Untyped Actor, along with the basic tool of DeathWatch, provide the kind of flexibility that you can turn to in order to add some cool functionality to your apps.

ReaperパターンではActorの基本機能(DeathWatch)を用いて, アプリケーションに対してシステムレベルの動作を追加する, 抽象的なレベルの仕組みを使用しました. UntypedActorDeathWatchの基本機能とともにあなたのアプリケーションに素晴らしい機能をもらたす柔軟性を提供します.

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4