$shibayu36->blog;

株式会社はてなでエンジニアをしています。プログラミングや読書のことなどについて書いています。

Play Frameworkのサーバ立ち上げ部分のコードを追いかける

Play Frameworkとsbtを使っていて、sbt runを実行した時にどういうことが起こっているのか、compileしたときのmain関数はどこにあるのかあたりが分かってなかった。この辺が分かってないと、開発でrunしている最中に変な挙動をした場合のデバッグや、本番アプリケーションサーバのチューニングなどがしづらい。そこで今回はPlay Frameworkのサーバ立ち上げ部分のコードを追いかけてみたのでメモ。あんまりsbtに詳しくないので、内容が間違っていたら教えてください。

playframeworkを使っているプロジェクトでbuild.sbtに書いていること

build.sbt書いている内容はこれだけ。

lazy val root = (project in file(".")).enablePlugins(PlayScala)

またproject/plugins.sbtには以下が書かれている。

addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.13")

これだけでrunやcompileがうまく動くようになってて不思議。

sbt runで立ち上がるもの

まずsbtでinspect runするとどこで定義されているか分かる。PlaySettingsにありそう。

[play-scala-slick-example] $ inspect run
...
[info] Defined at:
[info]  (play.sbt.PlaySettings.serviceSettings) PlaySettings.scala:105
...

https://github.com/playframework/playframework/blob/aa66958fa46b573bbf7cd8b92a92568c64c15cd4/framework/src/sbt-plugin/src/main/scala/play/sbt/PlaySettings.scala#L104..L106 あたりで、runしたときはPlayRun.playDefaultRunTaskを呼ぶようになってて、Compile/run/mainClassにplay.core.server.DevServerStartを設定している。

https://github.com/playframework/playframework/blob/aa66958fa46b573bbf7cd8b92a92568c64c15cd4/framework/src/sbt-plugin/src/main/scala/play/sbt/run/PlayRun.scala#L49..L50 playRunTaskを呼んでる。

https://github.com/playframework/playframework/blob/aa66958fa46b573bbf7cd8b92a92568c64c15cd4/framework/src/sbt-plugin/src/main/scala/play/sbt/run/PlayRun.scala#L79..L96 devModeServerの定義で先程のmainClassを使いつつ、Reloaderを使ってサーバを起動できるようにする。

https://github.com/playframework/playframework/blob/aa66958fa46b573bbf7cd8b92a92568c64c15cd4/framework/src/run-support/src/main/scala/play/runsupport/Reloader.scala#L226..L235 portの設定有無によって、先程渡したmainClassのmainDevHttpModeとかを使って立ち上げる。つまりデフォルトではplay.core.server.DevServerStart.mainDevHttpModeかな。

https://github.com/playframework/playframework/blob/aa66958fa46b573bbf7cd8b92a92568c64c15cd4/framework/src/play-server/src/main/scala/play/core/server/DevServerStart.scala#L39..L50 mainDevを呼ぶだけ。

https://github.com/playframework/playframework/blob/aa66958fa46b573bbf7cd8b92a92568c64c15cd4/framework/src/play-server/src/main/scala/play/core/server/DevServerStart.scala#L52..L58 あとはこのへんで頑張ってサーバを起動してますね。

こんな感じの流れっぽい。

compileやdistしたときのmain関数はどこか

sbt runは開発サーバが起動したが、compileした時は違うはず。調べてみるとこちらもPlaySettingsにかかれていた。

https://github.com/playframework/playframework/blob/aa66958fa46b573bbf7cd8b92a92568c64c15cd4/framework/src/sbt-plugin/src/main/scala/play/sbt/PlaySettings.scala#L152..L153 Compile/mainClassにplay.core.server.ProdServerStartが設定されている。

https://github.com/playframework/playframework/blob/aa66958fa46b573bbf7cd8b92a92568c64c15cd4/framework/src/play-server/src/main/scala/play/core/server/ProdServerStart.scala#L20..L26 普通にmain関数が定義されている。

こういう感じなので、https://www.playframework.com/documentation/aa66958fa46b573bbf7cd8b92a92568c64c15cd4/Deploying にかかれているようにdistやuniversal:packageZipTarballを実行して得られた実行ファイルを起動すると、ProdServerStartのmainから実行が始まるという雰囲気。

まとめ

playframeworkのsbt pluginを有効にしたときの、runやcompileの挙動周りを調べた。奥がだいぶ深かった。ちょっと知識が深まった気がする。