Scala IDEが便利になった

Scala IDEが便利になった。

というより、Eclipse同梱版が出来て、楽になった。

http://scala-ide.org/

Scala IDEは、Scala2.10.x用の環境で、Scala 2.9.x以下を使っている場合は使えないようです。

※Scala IDE上ではScala 2.10.xが動くけど、コマンドラインでは2.9.xという感じでは使えるかと思います。

Scala IDEは、ベースがEclipseなので、Windowsの他、Mac OSとLinux上で使えるようです。

Scala IDEについて

自分が便利だと思っていること

  • JavaとScalaのミックスプロジェクトが簡単に扱える
  • Scalaのコード補完が出来る
  • Scalaコードフォーマッティング
  • import自動追加
  • Play frameworkのテンプレートが普通に開ける
  • Play frameworkのテンプレート

Play frameworkのpidファイル「RUNNING_PID」ファイルの場所を変える(v2.0.4以降)

Play frameworkを本番環境にデプロイする場合に、PIDファイルを別の場所に置きたいなと思ったりします。

デフォルトだとプロジェクトディレクトリの直下にRUNNING_PIDというファイルが作成されます。

このRUNNING_PIDファイルを作る所のコードを確認すると

play.core.server.NettyServer

の中にあるようです。

[scala]
def createServer(applicationPath: File): Option[NettyServer] = {
// Manage RUNNING_PID file
java.lang.management.ManagementFactory.getRuntimeMXBean.getName.split(‘@’).headOption.map { pid =>
val pidFile = Option(System.getProperty("pidfile.path")).map(new File(_)).getOrElse(new File(applicationPath.getAbsolutePath, "RUNNING_PID"))

// The Logger is not initialized yet, we print the Process ID on STDOUT
println("Play server process ID is " + pid)

if (pidFile.getAbsolutePath != "/dev/null") {
if (pidFile.exists) {
println("This application is already running (Or delete "+ pidFile.getAbsolutePath +" file).")
System.exit(-1)
}

new FileOutputStream(pidFile).write(pid.getBytes)
Runtime.getRuntime.addShutdownHook(new Thread {
override def run {
pidFile.delete()
}
})
}
}
}
[/scala]

PIDファイルの場所をカスタマイズするには、pidfile.pathというオプションにPIDファイルのパスを指定すれば良いようです。

ということで、プロジェクトの起動パラメータとして
[text]-Dpidfile.path=/tmp/hogeproj.pid[/text]
という感じに指定します。

Apacheのログから、各IPからのアクセス数を集計する

Apacheのログから、各IPからのアクセス数を集計して、多い順にソートして表示するスクリプト
[scala]val apacheLog = new File("access.log")

/*
 * IPアドレスのカウント
 */
StringFileInput(apacheLog) { reader =>
  var count = 0

  // ランダム文字生成器
  val it3 = new Iterator[Array[String]] {
    def hasNext = reader.ready
    def next(): Array[String] = { reader.readLine.split(" ") }
  }

  val logAnalize = (Map[String, Int]() /: it3 ){(result, item) => result + (item.first -> (result.getOrElse(item.first, 0) + 1))}
  logAnalize.toList.sort((a, b) => a._2 < b._2).foreach{case (key, value) =>
    println(key, value)
  }

  // 61.126.188.190
  // (List[Array[String]]() /: it3){(result, item) => item :: result}.filter(a => {a.first == "61.126.188.190"}).map(a => println(a.mkString(" ")))
}[/scala]

Play Framework 2.0でログファイルローテートしてみる

Play Framework 2.0系のプロジェクトで、リクエスト毎にログを出力するような設定をした場合、ログファイルをローテートしたくなったりします。

場合によってはlogrotateの設定でやったりもします。

が、今回は、Play Frameworkのログの設定でローテートしてみます。

Play Frameworkは、LogBackというミドルウェアを使用してログを出力します。

で、必要な設定などはすでにPlay Framework自体が設定してくれているので

ログを出力するのは

[text]
Logger.debug("debug ok")
[/text]

という感じでログが出力出来ます。

ログはデフォルトでは
{プロジェクト}/logs/application.log
に出力されます。

デフォルトではログファイルはローテートしないので、カスタマイズして見ます。

カスタマイズするには

logger.xml(ファイル名は適当)というファイルをconf配下に作成します。

Play Framework本体からlogger.xmlファイルをコピーして持ってくるのが確実かもしれません。

その場合は、
{Play Framework}/framework/src/play/src/main/resources/logger.xml
をコピーして持ってきます。

application.logに出力する設定は、

[text]
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
[/text]

という部分になります。 LogBackの設定で、ローテートさせる設定はというと、 このclassの部分を「ch.qos.logback.core.rolling.RollingFileAppender」に変える事で対応出来るようなので、変更します。 さらに、ローテートする条件と、ローテート時のログファイル名をappenderのメンバーとして以下のように指定します。

[xml]
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${application.home}/logs/application.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>10</maxHistory>
</rollingPolicy>
[/xml]

fileNamePatternは、ローテート時のログファイル名
maxHistoryは、ヒストリ保持期間(日)

となります。

これで設定ファイルは完成しました。

続いて、このログの設定ファイルを起動時に読み込む設定ですが、これは起動時のパラメータになります。

startコマンドでやる場合は

[shell]
play "start -Dlogger.resource=logger.xml"
[/shell]

となります。

target/startの場合は

[shell]
target/start -Dlogger.resource=logger.xml
[/shell]

となります。

今回はapplication.logという名前のままでしたが、本番用と開発用でログファイル名を分けるのも良いかもしれません。

Scalaでちょっとスマートにファイルを読み込んでみる(その2)

前回、Scalaでちょっとスマートにファイルを読み込んでみるで、defで定義した物を、case classに変えてみました。
[scala]case class StringFileInput(file: File)(f: BufferedReader =&gt; Unit) {
val in = new FileInputStream(file)
try {
f(new BufferedReader(new InputStreamReader(in)))
} finally {
println("ファイルクローズ")
in.close
}
}

// 使用例
StringFileInput(currentFile) { reader =&gt;
while(reader.ready) {
println(reader.readLine)
}
}[/scala]

Scalaでちょっとスマートにファイルを読み込んでみる

Scalaだと、テキストファイルなどを開く時は
[scala]// ファイルを開いてインスタンスを得る
val src = Source.fromFile(currentFile)

// ファイルの行をforeach
src.getLines().foreach(println)

// ファイルを閉じる
src.close[/scala]
と書いたりしますが

なんか、最後にclose書かないと行けないのがスマートじゃ無いなと思ったりします。

なので、ちょっとこの辺をいじってみましょう。

参考として、Rubyの場合は、このように書けます。
[ruby]File.open("foo") {|f|
f.each_line {|line|
print line
}
}[/ruby]
ブロック内でファイル操作という感じですね。

ScalaでもRubyと同様にブロック内でファイル操作、という感じが出来れば嬉しいので

ファイルを開く->ブロック内で処理->ファイルを閉じるという事をするメソッドを書きます。
[scala][/scala]def stringFileInput(file: File)(f: BufferedReader => Unit) {
val in = new FileInputStream(file)
try {
f(new BufferedReader(new InputStreamReader(in)))
} finally {
in.close
}
}

// 使用例
stringFileInput(currentFile) { reader =>
while(reader.ready) {
println(reader.readLine)
}
}[/ruby]

Play Framework で、リクエストログを出力したい

リクエストを受けた直後のアクションについては

/app/Global.scala

というオブジェクトのファイルを作り

GlobalSettingsを継承します。

このGlobalSettingsというトレイトには

beforeStart
onStart
onStop
onRouteRequest
onError
onHandlerNotFound
onBadRequest

というメソッドが定義されていて、それぞれオーバーライドする事でアクションを設定する事が出来ます。

今回は、リクエストが来た時に処理させたいので
onRouteRequest
をオーバーライドして使います。

[scala]
import play.api._
import play.api.mvc._

object Global extends GlobalSettings {
/**
* リクエストが発生した時に実行される処理
*
* オーバーライドして、ログ出力後に親メソッド起動
*/
override def onRouteRequest(request: RequestHeader): Option[Handler] = {
Logger.info(request.toString)
super.onRouteRequest(request)
}
}
[/scala]

このように設定する事で、リクエストがあるたびにリクエスト情報を常にログに出力することが出来ます。

Play Framework で、SQLの実行ログもログに出力させたい場合

Play Frameworkはなんか、デフォルトで正常に動いていると、ログが少ないなと思ったりします。

正常に動いたように見えても、データがおかしいのを拾っている場合もあります。

そんな時は、どんなSQLが叩かれているのかと言うのを見たくなります。

Ruby on Railsでは、developmentモードで起動していると常にSQLの実行ログが出たりしますが

Play Frameworkでは設定を追加しないと駄目なようです。

設定は

application.conf

db関係の設定があると思うのですが
その設定と同列の設定で

[text]db.default.logStatements=true[/text]

というのを1行追加します。

さらに、

[text]# Root logger:
logger.root=ERROR[/text]

という部分を

[text]logger.root=DEBUG[/text]

に変更します。

これで、コンソールにログが出力されるようになります。

Play Framework 2.0.2 で負荷をかけるとエラーが出まくる

これは、Play Framework で使用しているAkkaのデフォルト設定による物で、ボディパーサのタイムアウトが1秒に設定されています。

retrieveBodyParserTimeout = 1 second

なので、負荷をかけていって、一つのリクエストが1秒を超えたあたりでタイムアウトのエラーが発生してしまいます。

そこで、Akkaの設定を追加してあげれば良いのですが、どこに追加したら良いかというと

決まりは無い感じですが

confの中にあるapplication.confとか
akka.confを作ってapplication.confincludeするかとかです。

今回は適当にapplication.confの一番下に付けちゃいます。
[text]play {
akka {
event-handlers = ["akka.event.slf4j.Slf4jEventHandler"]
loglevel = WARNING

actor {
deployment {
/actions {
router = round-robin
<span style="color: #ff0000;">nr-of-instances = 128</span>
}

/promises {
router = round-robin
nr-of-instances = 24
}

}

<span style="color: #ff0000;">retrieveBodyParserTimeout = 120 second</span>

actions-dispatcher = {
fork-join-executor {
<span style="color: #ff0000;">parallelism-factor = 100</span>
<span style="color: #ff0000;">parallelism-max = 512</span>
}
}

promises-dispatcher = {
fork-join-executor {
parallelism-factor = 1.0
parallelism-max = 24
}
}

websockets-dispatcher = {
fork-join-executor {
parallelism-factor = 1.0
parallelism-max = 24
}
}

default-dispatcher = {
fork-join-executor {
parallelism-factor = 1.0
parallelism-max = 24
}
}

}
}
}[/text]
適当にretrieveBodyParserTimeoutですが、適当に120秒と指定してみました。

長すぎですね。。

続いて、負荷をかけていくと、まだCPU、メモリ共にマシンに余裕がありそうな感じなので

Play Frameworkの処理スレッド数を増やしてみました。

actions-dispatcherという所の
parallelism-factorを100に
parallelism-maxを512に
設定してみました。

factorの方は、マシンのCPU数×factorの数値分スレッドを増やしますよ、という設定らしく、これも大きすぎですね。まぁいいや。

で、maxの方は、action-dispatcherでは、最大スレッド数をmaxの値までに制限しますよ、という事らしいです。
ようするに、512スレッドまでしか増えないという感じでしょうか。

スレッド数、いくつがちょうど良いんでしょうね。
環境毎に試行錯誤が必要な気がします。

それにしても、まだCPUには余裕があるのは何故なんだ。。

もう少しくらい負荷がかかっても余裕なのに。。

ScalaのArrayなどで、Rubyのeach_with_indexのようなインデックス付きイテレーションを使う

Rubyでは

[scala][‘one’, ‘two’, ‘three’].each_with_index |value, index|
p "#{index}番目は#{value}です"
end[/scala]

というような書き方が出来ます。

これをScalaで書くにはどうするか。

[scala]
val list = List("one", "two", "three")
list.zipWithIndex.foreach{ case(value, index) => println("%d番目は%sです".format(index, value)) }[/scala]

という感じになります。

さて、このzipWithIndexですが、ややパフォーマンスが悪いのが難点です。

このzipWithIndexというのは、scala.collection.ItelateLikeにあるメソッドなのですが
もう一つ、zipWithIndexscala.collection.Iteratorにも存在しています。
こちらの方はパフォーマンスが良いようで
こちらを使いたいと思います。

[scala]list.iterator.zipWithIndex.foreach{ case(value, index) => println("%d番目は%sです".format(index, value)) }[/scala]

と、listzipWithIndexの間にiteratorを入れるだけですね。

これでパフォーマンスが上がります。

Scalaの複数バージョンを切り替えられるsvm

Rubyにはrvmがある。

Scalaにはsvmがある。

ということで、svmを入れてみたいと思います。

https://github.com/yuroyoro/svm

ここから、cloneかzipファイルをダウンロードします。

展開した所にsvmがあると思うので、ここにパスを通します。

Ubuntuだと~/.profileの最後に

export PATH=$PATH:~/svm

もう一つ、

export SCALA_HOME=~/.svm/current/rt

とかやればOKです。

で、svmと叩いて、ヘルプが出力されれば完了です。

続いて、Scalaのインストールですが

本家のバージョンに則ってインストールします。

最新であれば、2.10.0なので

svm install 2.10.0

一つ前の2.9.2であれば

svm install 2.9.2

切り替える方法

svm 2.10.0

svm 2.9.2

と、非常に簡単です。

 

Play Frameworkでapplication.confではなく、別の設定ファイルを読み込んで起動する設定

application.confには前環境で共通の内容を設定
prod.confには、本番環境用のみのものを設定

という感じで、本番と開発で設定を分けたいなと思います。

これを実現するには

起動時のパラメータに以下のような設定を追加することで実現出来ます。
[text]config.resource=prod.conf[/text]
このconfig.resourceというのは、Play プロジェクト内のリソースとしてのprod.confを読み込みます、という設定なので、
confディレクトリに格納しておく必要があります。

confディレクトリに捕らわれずに別の場所に置きたいという場合は

config.fileにすればフルパスで設定することが出来ます。

[Scala]BigDecimalで四捨五入

今回のお題は
プログラムをやってて、ある程度数値をいじる方にはもう一般的な事かもしれませんが

四捨五入です。

Scalaで。

わかりやすいように例を挙げますと

画像サイズを比率を保ったまま縮小する処理を書いてみます。

 

このような感じで「widthAfter / widthBefore」で比率を求め、heightBeforeに掛け合わせて算出します。

これをそのまま実行するとheightAfter
339.9734395750332005312084993359895

というような、数値になってしまいます。

今回の想定では、340という整数が欲しかったので、小数点を四捨五入したいと思います。

これで340という数値が得られました。

このBigDecimalというクラスは、javaBigDecimalをラップしているようです。

使い方もほとんど同じですね。

ちなみに、
setScaleの一つ目の引数は四捨五入する位置で、0が小数点になります。
1を指定すれば、1.1のような、少数が一桁の数値になります。
二つ目の引数が、四捨五入、切り上げ、切り捨ての指定をします。
四捨五入の場合は
scala.math.BigDecimal.RoundingMode.HALF_UP
切り上げの場合は
scala.math.BigDecimal.RoundingMode.CEILING
切り捨ての場合は
scala.math.BigDecimal.RoundingMode.FLOOR

[Scala]Play framework 2.0 のwithSessionを使うと今まで入れてたセッションが消える

APIDOCとか、Documentをみたら、分かる事ではあるが、はまりやすいので書いておく

とした場合に

action1を叩いてからaction2を叩くと
セッションの情報がtest2 -> value2だけになる。

test1とtest2のセッションを保持したいなら

とする

たぶん

[Scala]Play framework で使用するJVMのヒープサイズを環境変数で指定する

Scalaの開発をVMで行っているわけでありますが

素のPlay frameworkだと、色々やってるうちにメモリを浪費してしまうようです。

メモリリークでは無いけど、必要なメモリをどんどん確保してしまうみたい。

なので、ヒープの上限という意味で割り当てを指定してみます。

毎回Play起動時に指定するのは面倒なので、環境変数で。

#~/.bashrc を開いて、一番下あたりに

と書き込んでターミナルを再起動します。

以上です。

playを起動すると

Picked up _JAVA_OPTIONS: -Xms256M -Xmx256M
という行がコンソールに出てきたと思います。

[Scala]javaのIteratorなデータをScalaで簡単にforeachする

とかやりたい時に、そのままやってもエラーになってしまうが

を書いておくと、上記の書き方が出来るようになる

※補足
sbtでプロジェクトを管理している時にApache Commons IOを使う場合

build.sbtに

を追加しておくと依存関係が解決

[Scala]Play framework 2.0で、playのコンソールに入らずにアプリを実行する

Play frameworkは便利ですが、そのまま稼働させたい時などにわざわざ

なんて打ちたくありません。

でも、playコマンドにそのままstartと引数を付けて叩くと、コマンド一発で起動する事が出来ます。

ところが、

なんてやった場合に、ポートが80番にならない。

よくよく調べてみると、playコマンドに対してstartと80の引数が渡された状態のようで

playはstartだけを有効な引数として見るっぽいです

なので、

playに渡す引数は一つにする必要があって

とやって上げる事で

“start 80”

と認識してくれます。