Akka実践バイブルをゆっくり読み解く企画の第7章です。
第7章 設定とロギングとデプロイ
ScalaやAkkaに限らず、設定周りって誰かが準備してくれていたりすることが多いので、設定全体をしっかりと把握していることは決して多くないです。特にログ周りとか、新規でプロジェクトを作ることがあっても、既存のうまくいっている設定をコピペしてしまうこともあるし・・・
これを気にしっかり反省しないといかんです。
設定
Akka独自の設定方法があるとかってわけではなく、Scalaの標準の設定方法に従ってAkkaの設定を行う。
TypesafeConfigによる設定
Typesafe Config
はScala標準の設定ライブラリ。複数のファイルフォーマットをサポートしており、優先順位としては以下の順番で読み込まれる。
- application.conf
- application.json
- application.properties
confファイルは前回から普通に出てきているけど、可読性が高いのがとても良い。
MyAppl {
# MyAppl の version
version = 10
database {
# MyAppl の database の connect
connect = "jdbc:xxx://xxxxxx/xxxx"
# MyAppl の database の user
user = "user"
}
}
// confファイルの読み込み
val config = ConfigFactory.load("myAppl")
// MyAppl の version
val version = config.getInt("MyAppl.version")
val db = config.getConfig("MyAppl.database")
// MyAppl の database の connect
val conn = db.getString("connect")
// MyAppl の database の user
val user = db.getString("user")
// フルパスでの読み取りも可
val user2 = config.getString("MyAppl.database.user")
conf、json、propertiesのいずれにもパラメータが設定されていない場合のためのフォールバックの設定をreference.conf
に持たせることができる。
Akkaの設定
アクターを生成する際に、デフォルトの設定が読み込まれる。
// アクターシステムの生成
val myActor = ActorSystem("myActor")
// この場合、上記TypesafeConfigのルールに則ってapplication.configなどが読み込まれる
val myConfig = myActor.settings.config
以下のようにすると、読み込む設定ファイルを指定することもできる。
// 対象の設定ファイルを読み込む
val config = ConfigFactory.load("myAppl")
// アクターシステムの生成時に、設定情報を渡す
val myActor = ActorSystem("myActor", config)
// myAppl.configの情報を読み込むことができる
val myConfig = myActor.settings.config
また、以下のようにすることで設定ファイルをインクルードすることもできる。
# includedFile.confがインクルードされる
include "includedFile"
MyAppl {
# includedFile.confにversionが設定されていても、10で上書きされる
version = 10
}
より複雑な設定の例として、以下のようなことも可能。
"can be lifted" in {
// lift.confから読み込み
val configuration = ConfigFactory.load("lift")
// フォールバックとしてconfigurationの内容を追加
val mySystem =
ActorSystem("myFirstLiftTest",
configuration.getConfig("myTestLift").withFallback(configuration))
イメージとして以下のようになる。
myTestLift {
myTest {
applicationDesc="My Lift Test" // ①
}
rootParam="root" // ②
}
myTest {
intParam=20 // ③
applicationDesc="My Default Test" // ④
}
myTestLift {
myTest {
applicationDesc="My Lift Test" // ①
}
rootParam="root" // ②
}
myTest {
intParam=20 // ③
applicationDesc="My Lift Test" // ⑤ ★設定値の上書きが発生★
}
rootParam="root" // ⑥
実際の動きとしては、以下のようになる。
"can be lifted" in {
...
val config = mySystem.settings.config
// ③の設定内容
config.getInt("myTest.intParam") must be(20)
// フォールバックによって、④の内容が⑤の内容で上書き
config.getString("myTest.applicationDesc") must be("My Lift Test")
// フォールバックによって、⑥の設定が追加される
config.getString("rootParam") must be("root")
// myTestLiftがlift.confから消えるわけではないので、元々の設定値にアクセスすることもできる
config.getString("myTestLift.rootParam") must be("root")
ロギング
基本的な使い方
基本的にはJavaの時と同じように考えればOKとのこと。
Akkaのロギングシステムは特定のロギングライブラリに依存しないように、ロギングアダプタを提供することにより複数のロギングライブラリをサポートしている。あと、ロギング時のI/O待ちにによるブロックを防ぐためにノンブロッキングな仕組みで提供される。
ロギングのイベントハンドラの設定は以下のように行う。
akka {
# 標準出力を使用
loggers = ["akka.event.Logging$DefaultLogger"]
# SLF4Jを使用
loggers = ["akka.event.slf4j.Slf4jLogger"]
# Options: ERROR, WARNING, INFO, DEBUG
loglevel = "DEBUG"
}
提供されているロギングの仕組みだけでは不十分な場合は、独自のイベントハンドラを定義することも可能。
イベントハンドラの設定を行ったら、あとは以下の方法でロギングを実現することができる。
import akka.event.Logging
// 第2引数の設定内容が出力元を示す。
// 第2引数に設定可能なのは、以下のとおり。
// String
// Actor/ActorRef
// Class
val log = Logging(context.system, this)
Logging
によるloggerインスタンスの生成は、ActorLogging
トレイトにお任せすることもできる。
class MyAcotr extends Actor with ActorLogging {
// Loggingの第2引数には自Actorが設定されるため、出力元はMyActorとなる
}
以下、ActorLogging
トレイトの実装内容。Loggingの第2引数には自Actorが設定されているのがわかる。
trait ActorLogging { this: Actor ⇒
private var _log: LoggingAdapter = _
def log: LoggingAdapter = {
// only used in Actor, i.e. thread safe
if (_log eq null)
// ★Loggingにはthisが設定される★
_log = akka.event.Logging(context.system, this)
_log
}
}
アクターが受け取るメッセージを記録する
アクターが受信したメッセージをログに出力することもできる。
class MyActor extends Actor with ActorLogging {
def receive = LogginigReceive {
case ... => ...
}
}
akka {
debug {
# これによって受信したメッセージをログに出力する設定が有効になる
receive = on
}
}
デプロイ
スタンドアロンアプリケーション作成に向けた設定
スタンドアロンアプリケーションの作成には、sbt-native-packager
プラグインを使用する。
plugins.sbt
// 2018/03/20時点の`sbt-native-packager`の最新バージョンは1.3.3
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.3")
sbt-native-packager
プラグインを使用する場合は、設定ファイルを<project home>/src/universal/conf
配下に配置する必要がある。
build.sbt
// nameに設定された名称が起動スクリプトのファイル名になる
name := "deploy"
version := "1.0"
organization := "manning"
scalacOptions ++= Seq(
"-deprecation",
"-unchecked",
"-Xlint",
"-Ywarn-unused",
"-Ywarn-dead-code",
"-feature",
"-language:_"
)
// スタンドアロンアプリケーションであることを宣言
enablePlugins(JavaAppPackaging)
// 追加しないとapplication.confやlogback.xmlが見つからない。
// stageした際に出力されるstage/binから見たconfの場所を示す。
scriptClasspath +="../conf"
libraryDependencies ++= {
val akkaVersion = "2.5.4"
Seq(
"com.typesafe.akka" %% "akka-actor" % akkaVersion,
"com.typesafe.akka" %% "akka-slf4j" % akkaVersion,
"ch.qos.logback" % "logback-classic" % "1.0.13",
"com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test",
"org.scalatest" %% "scalatest" % "3.0.0" % "test"
)
}
起動スクリプトの生成
CLIで以下のように入力する。
> sbt (git)-[master]
[info] Loading settings from plugins.sbt ...
[info] Loading project definition from /home/xxxxxx/workspace/scala/akka-in-action/chapter-conf-deploy/project
[info] Loading settings from build.sbt,scala.sbt ...
[info] Set current project to deploy (in build file:/home/xxxxxx/workspace/scala/akka-in-action/chapter-conf-deploy/)
[info] sbt server started at local:///home/xxxxxx/.sbt/1.0/server/f8bc3ba3406f8bf12708/sock
sbt:deploy> stage
[info] Packaging /home/xxxxxx/workspace/scala/akka-in-action/chapter-conf-deploy/target/scala-2.12/deploy_2.12-1.0-sources.jar ...
[info] Done packaging.
[info] Wrote /home/xxxxxx/workspace/scala/akka-in-action/chapter-conf-deploy/target/scala-2.12/deploy_2.12-1.0.pom
[info] Main Scala API documentation to /home/xxxxxx/workspace/scala/akka-in-action/chapter-conf-deploy/target/scala-2.12/api...
[info] Packaging /home/xxxxxx/workspace/scala/akka-in-action/chapter-conf-deploy/target/scala-2.12/deploy_2.12-1.0.jar ...
[info] Done packaging.
model contains 7 documentable templates
[info] Main Scala API documentation successful.
[info] Packaging /home/xxxxxx/workspace/scala/akka-in-action/chapter-conf-deploy/target/scala-2.12/deploy_2.12-1.0-javadoc.jar ...
[info] Done packaging.
[success] Total time: 6 s, completed 2018/03/20 22:28:45
stage
を実行したら、以下のディレクトリにbin/conf/libが出力される。
> pwd
/home/xxxxxx/workspace/scala/akka-in-action/chapter-conf-deploy/target/universal/stage
> ls
bin conf lib
stage
ディレクトリ配下に生成されるディレクトリは以下のような構成になっている。
ディレクトリ | 格納リソース |
---|---|
bin | Windows用/Unix用の起動スクリプトを含む |
lib | アプリケーションが依存している全てのjarファイルを含む |
conf | アプリケーションの全ての設定ファイルを含む |
bin
の中に起動スクリプトが入っているとのことなので、bin
の中を確認してみると以下のようになっている。
> pwd
/home/xxxxxx/workspace/scala/akka-in-action/chapter-conf-deploy/target/universal/stage/bin
> ls
deploy deploy.bat
起動スクリプトを以下のように実行することとでスタンドアロンアプリケーションを起動することができる。
./deploy
個人的まとめ
第7章はAkkaやアクターに関する要素はほとんどなかったけど、大事なことが書いてある章だった。どのような設定でアプリケーションが動いているのかを把握することは当然のこと。普段は大量のXMLや散らばった設定ファイルを紐解くのがシンドくてDB接続やログ出力先を雑に把握する程度だったけど、ちゃんと設定は把握しないといけないなぁと、本書の主題とは全然違うところでたいへん意識向上させられる章でした。