モーダルを閉じる工作HardwareHub ロゴ画像

工作HardwareHubは、ロボット工作や電子工作に関する情報やモノが行き交うコミュニティサイトです。さらに詳しく

利用規約プライバシーポリシー に同意したうえでログインしてください。

工作HardwareHub ロゴ画像 (Laptop端末利用時)
工作HardwareHub ロゴ画像 (Mobile端末利用時)

Akka HTTP Scala アプリケーションのデーモン化

モーダルを閉じる

ステッカーを選択してください

モーダルを閉じる

お支払い内容をご確認ください

購入商品
」ステッカーの表示権
メッセージ
料金
(税込)
決済方法
GooglePayマーク
決済プラットフォーム
確認事項

利用規約をご確認のうえお支払いください

※カード情報はGoogleアカウント内に保存されます。本サイトやStripeには保存されません

※記事の執筆者は購入者のユーザー名を知ることができます

※購入後のキャンセルはできません

公開日公開日
2016/03/16
最終更新最終更新
2021/10/07
記事区分記事区分
一般公開

目次

    アカウント プロフィール画像 (サイドバー)

    Scalaはいいぞ

    0
    ステッカーを贈るとは?

    Akka HTTP Scala アプリケーションをデーモン化します。

    参考サイト

    インストール

    前提

    • Linux マシン
    • fat JAR を生成できるビルドツール (ここでは sbt-assembly を使用します)

    Java のインストール

    yum または Oracle のサイトからダウンロードしてインストールします。Akka の動作には Java 8 以降が必要です。

    sudo yum install java-1.8.0-openjdk-devel
    

    JCVS のインストール

    こちらから最新の commons-daemon-x.x.x-src.tar.gz をダウンロードして解凍します。

    tar zxvf commons-daemon-1.0.15-src.tar.gz
    

    公式サイトの手順にしたがってビルドします。JCVS はサブディレクトリに格納されています。

    The sources are located in the src/native/unix subdirectory.

    cd commons-daemon-1.0.15-src/src/native/unix/
    

    必要なソフトウェアをインストールしておきます。

    sudo yum install autoconf
    sudo yum install gcc-c++
    

    ビルドします。

    sh support/buildconf.sh
    ./configure --with-java=/usr/lib/jvm/java-1.8.0
    make
    sudo mv jsvc /usr/local/bin/
    

    動作確認します。

    $ jsvc -help | head
    Usage: jsvc [-options] class [args...]
    
    Where options include:
    
        -help | --help | -?
            show this help page (implies -nodetach)
        -jvm <JVM name>
            use a specific Java Virtual Machine. Available JVMs:
                'server'
        -client
    

    sbt の設定

    最新のバージョンは The Central Repository で検索してください。

    libraryDependencies += "commons-daemon" % "commons-daemon" % "1.0.15"
    

    Akka HTTP Scala アプリケーションを用意

    build.sbt

    lazy val root = (project in file(".")).
      settings(
        name := "myapp",
        version := "1.0",
        scalaVersion := "2.11.7",
        mainClass in assembly := Some("myapp.Main"),
        retrieveManaged := true,
        libraryDependencies ++= Seq(
          "com.typesafe.akka" %% "akka-http-core" % "2.4.2",
          "com.typesafe.akka" %% "akka-http-experimental" % "2.4.2",
          "ch.qos.logback" % "logback-classic" % "1.1.3",
          "org.slf4j" % "slf4j-api" % "1.7.12",
          "commons-daemon" % "commons-daemon" % "1.0.15"
        )
      )
    

    src/main/resources/logback.xml

    <configuration>
      <property name="LOG_DIR" value="./log" />
      <!-- <property name="LOG_DIR" value="/var/log/myapp" /> -->
      <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_DIR}/myapp.log</file>
        <append>true</append>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
          <!-- daily rollover  -->
          <fileNamePattern>${LOG_DIR}/myapp.%d{yyyy-MM-dd}.log</fileNamePattern>
          <!-- keep 90 days' worth of history -->
          <maxHistory>90</maxHistory>
        </rollingPolicy>
        <encoder>
          <pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
        </encoder>
      </appender>
      <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
          <pattern>%msg%n</pattern>
        </encoder>
      </appender>
      <root level="info">
        <appender-ref ref="FILE" />
        <appender-ref ref="STDOUT" />
      </root>
    </configuration>
    

    src/main/scala/Main.scala

    package myapp
    
    import org.apache.commons.daemon._
    import org.slf4j.LoggerFactory
    
    trait ApplicationLifecycle {
      def start(): Unit
      def stop(): Unit
    }
    
    class ApplicationDaemon extends Daemon {
    
      def init(daemonContext: DaemonContext): Unit = {}
    
      val app: ApplicationLifecycle = new Application
      def start() = app.start()
      def stop() = app.stop()
      def destroy() = app.stop()
    }
    
    object Main {
      def main(args: Array[String]): Unit = {
        val logger = LoggerFactory.getLogger("Main")
        val app = new ApplicationDaemon
        app.start()
        logger.info("Press RETURN to stop...")
        scala.io.StdIn.readLine()
        app.stop()
      }
    }
    

    src/main/scala/Application.scala

    package myapp
    
    import akka.actor.ActorSystem
    import akka.stream.ActorMaterializer
    import akka.http.scaladsl.server.Directives._
    import akka.http.scaladsl.Http
    import scala.concurrent.Future
    import org.slf4j.LoggerFactory
    
    class Application extends ApplicationLifecycle {
    
      val logger = LoggerFactory.getLogger("Application")
      val applicationName = "myapp"
    
      implicit val system = ActorSystem(s"$applicationName-system")
      implicit val materializer = ActorMaterializer()
      implicit val ec = system.dispatcher
    
      var started: Boolean = false
      var bindingFuture: Future[Http.ServerBinding] = null
    
      def start(): Unit = {
        logger.info(s"Starting $applicationName Service")
        if (!started) {
          val route = path("") {
            get {
              logger.info("ok")
              complete("ok")
            }
          }
          bindingFuture = Http().bindAndHandle(route, "0.0.0.0", 8080)
          logger.info("Server online at http://127.0.0.1:8080/")
          started = true
        }
      }
    
      def stop(): Unit = {
        logger.info(s"Stopping $applicationName Service")
        if (started) {
          bindingFuture.flatMap(_.unbind()).onComplete(_ => system.terminate())
          started = false
        }
      }
    }
    

    ビルド

    $ sbt assembly
    

    実行例

    $ java -jar target/scala-2.11/myapp-assembly-1.0.jar
    Starting myapp Service
    Server online at http://127.0.0.1:8080/
    
    $ curl http://127.0.0.1:8080
    ok
    
    $ cat log/myapp.log 
    2016-03-17 01:14:00,511 INFO [run-main-0] Application [Application.scala:24] Starting myapp Service
    2016-03-17 01:14:01,652 INFO [run-main-0] Application [Application.scala:34] Server online at http://127.0.0.1:8080/
    2016-03-17 01:14:02,909 INFO [myapp-system-akka.actor.default-dispatcher-5] Application [Application.scala:29] ok
    

    init.d スクリプトの作成

    事前準備

    sudo mkdir /var/log/myapp
    sudo mkdir /var/run/myapp
    sudo mkdir /usr/local/myapp
    sudo cp sbt-dir/src/main/resources/logback.xml /usr/local/myapp/
    sudo sed -e 's/.\/log/\/var\/log\/myapp/' /usr/local/myapp/logback.xml
    sudo cp sbt-dir/target/scala-2.11/myapp-assembly-1.0.jar /usr/local/myapp/
    

    /etc/init.d/myapp

    詳細は OS によって異なります。以下では CentOS 6 の例を記載します。

    #!/bin/sh
    #
    # myapp - sample application
    #
    # chkconfig: 345 55 45
    # description: sample daemon.
    #
    
    . /etc/rc.d/init.d/functions
    
    if [ -z "$JAVA_HOME" ]; then
        JAVA_HOME=/usr/lib/jvm/java-1.8.0
    fi
    JAVA_OPTS="-Xms512m -Xmx1024m"
    
    APP=myapp
    DAEMON_USER=root
    RETVAL=0
    
    PID=/var/run/$APP/$APP.pid
    OUT_LOG=/var/log/$APP/${APP}_out.log
    ERR_LOG=/var/log/$APP/${APP}_err.log
    
    APP_HOME=/usr/local/$APP
    APP_LOG_CONFIG=$APP_HOME/logback.xml
    APP_CLASSPATH=$APP_HOME/myapp-assembly-1.0.jar
    APP_CLASS=myapp.ApplicationDaemon
    
    if [ -n "$APP_LOG_CONFIG" ]; then
        JAVA_OPTS="-Dlogback.configurationFile=${APP_LOG_CONFIG} ${JAVA_OPTS}"
    fi
    JSVC=/usr/local/bin/jsvc
    
    DAEMON_ARGS="-home ${JAVA_HOME} ${JAVA_OPTS} -pidfile ${PID} -user ${DAEMON_USER}"
    DAEMON_ARGS="$DAEMON_ARGS -outfile ${OUT_LOG} -errfile ${ERR_LOG}"
    DAEMON_ARGS="$DAEMON_ARGS -cp ${APP_CLASSPATH} ${APP_CLASS}"
    
    start () {
        echo -n $"Starting $APP: "
        chown $DAEMON_USER /var/run/$APP
        chown $DAEMON_USER /var/log/$APP
        $JSVC ${DAEMON_ARGS}
        RETVAL=$?
        if [ $RETVAL -eq 0 ]; then
            success
        else
            failure
        fi
        echo
    }
    
    stop () {
        echo -n $"Stopping $APP: "
        $JSVC -stop ${DAEMON_ARGS}
        RETVAL=$?
        if [ $RETVAL -eq 0 ]; then
            success
        else
            failure
        fi
        echo
    }
    
    restart () {
        stop
        start
    }
    
    case "$1" in
        start)
            start
            ;;
        stop)
            stop
            ;;
        status)
            status -p $PID $APP
            ;;
        restart)
            stop
            start
            ;;
        *)
            echo $"Usage: $0 {start|stop|status|restart}"
            RETVAL=1
            ;;
    esac
    
    exit $RETVAL
    
    0
    詳細設定を開く/閉じる
    アカウント プロフィール画像 (本文下)

    Scalaはいいぞ

    記事の執筆者にステッカーを贈る

    有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。

    さらに詳しく →
    ステッカーを贈る コンセプト画像

    Feedbacks

    Feedbacks コンセプト画像

      ログインするとコメントを投稿できます。

      関連記事

      • Scala 文字列の処理
        書式指定 object Main { def main(args: Array[String]): Unit = { println("%d + %d = %d".format(1, 1, 2)) //=> 1 + 1 = 2 } } 文字列の比較 ヒアドキュメント 他の言語でいう「ヒアドキュメント」のようなものは """ で囲うことで実現できます。 object Main ...
        したくんしたくん6/18/2018に更新
        いいねアイコン画像0
      • Scala 日付に関する処理
        Date クラスを文字列にフォーマット import java.util.Date object Main { def main(args: Array[String]): Unit = { // format は Date に限らない文字列用の機能です。 println("%d-%d-%d" format (1, 1, 1)) //=> 1-1-1 printl...
        したくんしたくん6/5/2018に更新
        いいねアイコン画像0
      • 酢豚の基本的な使い方 (sbt)
        sbt は Scala および Java を主な対象としたビルドツールです。Scala Build Tool の略ではありませんが、Simple Build Tool という明示的な記述も公式ドキュメントなどには見当りません。以下 sbt の基本的な使用例をまとめます。使用した sbt のバージョンは 0.13 です。 公式ドキュメント [sbt 0.13](http://www.scala-sb...
        ねこねこ6/30/2018に更新
        いいねアイコン画像0
      • Scala 関数のサンプルコード
        「デフォルト引数」および「Unit 型を返す関数」 object HelloWorld { def main(args: Array[String]): Unit = { def myPrint(myArg: String = "default_value") = println(myArg + "!") val result = myPrint() //=> defau...
        したくんしたくん6/26/2018に更新
        いいねアイコン画像0
      • Scala 組み込みの制御構造
        if-else 条件分岐で知られる if-else は三項演算子のようにも使用されます。 object HelloWorld { def main(args: Array[String]): Unit = { val myVal = if (!args.isEmpty) args(0) else "default" println(myVal) } ...
        したくんしたくん10/7/2021に更新
        いいねアイコン画像0