Furyu

[フリュー公式] Tech Blog

フリュー株式会社の技術ブログです

2016年03月24日

sasaki

Play Framework 2.1系と2.3系に依存するライブラリのクロスビルド

ソーシャルゲーム2部 佐々木です。最近はなかなかコーディングの時間が取れず、ストレスフルな日々を送っています。今回は、半年くらい前に行った Play Framework に関する話題です。

背景

経緯から説明すると、Play Framework 2.1系であるサービス(以後、サービスA)を開発・運用していました。 そのサービスのシステムを流用して新たにサービス(以後、サービスB)を立ち上げることになり、その機会に Play Framework 2.3系に移行することにしました。 ただ、大きなシステム変更があるわけではなく、サービスAとサービスBでは特に大きな機能改修はありませんでした。 そこで、サービスAのシステムの中で再利用性の高い機能をライブラリ化して、サービスA/Bそれぞれがそのライブラリに依存するような形にすれば保守管理しやすいと考えました。

クロスビルドできない

いざ、Build.scala でクロスビルドの設定をしてみると、コンパイルエラーになってしまいました。

ここで問題になったのが、Play Framework の互換性でした。 Play Framework のリリースノートや移行ガイドを見るとわかりますが、Play Framework 2.2系から play.api.mvc.Result 関連のクラスがdeprecatedになり、Play Framework 2.3系では完全に取り除かれました。それにより、単純に依存するライブラリのバージョンを変えるようなクロスビルドの設定だけでは対応できませんでした。

ソースコードのパスを切り替える

そこで、Play Framework 2.1系とPlay Framework 2.3系でソースコードのパスを別々に設定することにしました。

この設定は、Play Framework 2.1系(Scala 2.10.4)でビルドする時には、src/main/scalasrc/main/play21/scalaをコンパイル対象とし、Play Framework 2.3系(not Scala 2.10.4)でビルドする時には、 src/main/scalasrc/main/play23/scalaをコンパイル対象とするものです。

あとは src/main/play--/scalaにそれぞれのplay.api.mvc.Result関連のクラスを定義することによって、当初の目的を達成することができました。

まとめ

WEBフレームワークやライブラリを使う側としては、バージョンアップや互換性にどう向き合うかは、悩ましいところでもあり面白いところでもあると思います。今回の件では、ソースコードのパスを切り替えるという発想に至るまでは紆余曲折あり、互換性を無視したバージョンアップをした Play Framework を憎んだこともありました(笑)が、今となってはこの方法以外無いのではと思っています。別の方法があればぜひぜひ教えていただきたいです!


2013年02月15日

Nginx+Play2.0利用時のNginxとAkkaのパフォーマンスチューニング

初めまして。ソフトウェア開発部の辻です。 Nginxと、Play2.0でのAkkaのパフォーマンスチューニングを行いました!

パフォーマンスチューニングを行った経緯

弊社のPlay2.0+Nginxを使ったWebアプリで、CPU使用率が10%に満たないにも関わらず、Akkaのタイムアウト(akka.pattern.AskTimeoutException)が頻発していました。 Play2.0では、並列処理行うために内部でAkka Actorを使用しており、このAkkaのタイムアウトは、このAkka Actorが長時間ブロックされたときに発生します。

そこで、テスト環境にてAkkaのタイムアウト設定を変更してみました。 タイムアウト設定はconf/application.confの以下の部分を編集することで変更できます。

タイムアウト設定を5秒にして負荷をかけてみたところCPU使用率が上がりましたが、2秒にして負荷をかけなおすとAkkaのタイムアウトが発生するようになりCPU使用率が下がりました。 このことから、タイムアウト設定を延ばせば、CPUに負荷をかけタイムアウトを減らすことができると分かりました。

ところが、弊社Webアプリは5秒以内にレスポンスを返さないといけないという制約があり、タイムアウト設定をこれ以上延ばすことはできません。 タイムアウト設定は延ばさずに、アプリケーションのレスポンス速度をあげる(レスポンス速度を上げるためにCPUを使うようにする)ために以下のような負荷試験を実施し、チューニングを行いました。

テスト環境

  • Amazon EC2 m1.large (4コア)
  • Play 2.0.2
  • Nginx 1.2.0
  • Akkaのタイムアウト設定は4秒とする

負荷設定

JMeterで、アクセスが多いと思われるページに以下の設定で負荷をかけました。 チューニング前の状態でAskTimeoutExceptionが頻発するように調整しました。

アクセス先 秒間アクセス数 ループ回数
ページA 30 100
ページB 30 100
ページC 20 100

上記の負荷をかけた時のタイムアウト回数(AskTimeoutException発生回数)をカウントし、このアクセスに対してレスポンスを返しきるまでの所要時間を計測します。

Playの設定

conf/application.confに以下の設定を記述することで、Play内部のAkkaの設定を上書きできます。 今回設定したのは以下の3つです。

項目名 説明 デフォルトの値
nr-of-instances ルータに登録するActorの数。 24
parallelism-factor CPU数*この値=Akkaで使用されるスレッド数 1.0
parallelism-max Akkaで使用される最大スレッド数 24
以下のページを参考にしました。

Scala/Akka Actor
【意訳】データベース駆動アプリのための、Play2最適化について

Nginxの設定

conf/nginx.confに以下の設定をします。

項目名 説明 デフォルトの値
worker_processes Nginxのworkerプロセス数。CPU数に合わせる。 1
以下のページを参考にしました。

hnginx設定 | 楽しく情報処理技術者試験
nginx連載3回目: nginxの設定、その1

チューニング

  1. チューニング前の設定のまま
  2. Nginxのworker_processesをCPU数に合わせる
  3. Akkaのスレッド数を増やす (上記までの参考ページから、とりあえず400がmaxになるように設定してみました。)
  4. 2,3両方を適用する

という順で設定してみました。

  Play Nginx 計測結果 結果
1 nr-of-instances = 24
parallelism-factor = 1.0
parallelism-max = 48
worker_processes 1 AskTimeoutException:490回
所要時間:3分12秒
2 nr-of-instances = 24
parallelism-factor = 1.0
parallelism-max = 48
worker_processes 4 AskTimeoutException:454回
所要時間:3分17秒
タイムアウトの発生回数が
減っていることから、
アプリの処理速度が上がった
3 nr-of-instances = 400
parallelism-factor = 100
parallelism-max = 400
worker_processes 1 AskTimeoutException:7回
所要時間:3分9秒
タイムアウトの発生回数が
減っていることから、
アプリの処理速度が上がった
4 nr-of-instances = 400
parallelism-factor = 100
parallelism-max = 400
worker_processes 4 AskTimeoutException:1回
所要時間:3分8秒
タイムアウトの発生回数が
減っていることから、
アプリの処理速度が上がった

所要時間が延びることなく、AskTimeoutExceptionの発生回数が減少しているので、 Nginxのworker_processesをCPU数に合わせ、Akkaのスレッド数を増やすことでアプリの処理速度を上げることができると分かりました。

まとめ

  • Akkaのタイムアウト時間を延ばせばCPUに負荷がかかります。弊社のWebアプリの制約上タイムアウト時間を短く設定していたので、CPUに負荷がかかる前にタイムアウトになっていました。
  • Nginxのworkerを増やし、Playのスレッド数を増やすことでタイムアウトになる前にCPUに負荷をかけてアプリの処理速度をあげることができました。
  • Akkaのスレッド数はまだこの値が適切かどうかはわかりません。今後のアクセス状況やタイムアウトの発生頻度などを見て再度チューニングする必要があります。

以上です。


2012年10月9日

kunihira

play-velocity-plugin公開!

皆様、お久しぶりです。

国平です。

さてこの度、弊社で開発し実際のプロダクト上で利用されているplay-velocity-pluginをオープンソース化することができました。

play-velocity-plugin

弊社では初のOSSプロジェクトとなります。

play-velocity-pluginはその名の通り、Play!Framework2 上で動作するVelocityTemplateプラグインです。

今日は、このプロジェクトのメイン開発者直々の押し付…ぢゃない、指名を受けてプラグインの紹介をさせて頂きます。

前回の記事にあります通り、弊社では現在ソーシャルゲーム開発において、Play!Framework2とScalaを用いた開発を行なっています。

そのことについては、粕谷がPlay!Framework勉強会第3回で導入事例を発表させていただきました。

Play勉強会 第3回 from Daisuke Kasuya

この資料でも、play-velocity-pluginの公開については言及されていますが、ついに実現しました。

今回は、このプラグインを導入によるメリットについて宣伝させていただこうと思います。

なぜScalaTemplateをやめたのか

Play!Frameworkは基本的にはフルスタックのWebフレームワークです。

そのため、テンプレートエンジンについてもデフォルトでScalaTemplateを利用することができます。

Play! The template engine

このScalaTemplateの特長として、

  • Scalaの強力な構文が利用できる
  • コンパイルされることによりバグが回避される

といったメリットがあります。

特に、コンパイル時にきっちりと変数がセットされることや、変数の型、メソッドやフィールド呼び出しの記述ミスがチェックされるので、バグの入り込むスキを少なくしてくれます。

しかし、粕谷の資料でも言及されているとおりトレードオフとして、

  • Scala構文がデザイナによる変更の容易性を下げている 
  • HTMLのちょっとした変更でもコンパイルが要求される

という問題が発生してしまいます。

その他にも、ScalaTemplate採用プロジェクトがある程度進行した時点で起こった問題として、テンプレートファイルがすべてコンパイル対象となってしまうので、コンパイルの際に非常に多くのマシンリソースが要求されてしまいました。

3年前のPCのマシンスペックではコンパイルに10~15分かかることもあったようです。

更には、デフォルトのJVMへのメモリ割り当てでは容量が足りずコンパイルが失敗するため、ビルドスクリプトを直接編集して利用メモリを増やすという対応も必要となりました。

このような問題があり弊社で以前から利用実績のあるVelocityをテンプレートエンジンとして採用することとなりました。

Velocityの特長

VelocityはApacheFoundationで公開されているJavaベースのテンプレートエンジンです。

Apache Velocity

もともと、弊社ではPlay!Frameworkの採用以前からS2ファミリーを利用しており、テンプレートエンジンとしてS2Velocityを利用していました。

このVelocityなのですが、特長として

  • スクリプト言語ライクに動作する(コンパイル不要) 
  • Webデザイナにも変更がしやすい(構文がわかりやすい)

という特長があります。

もちろん、トレードオフとして、

  • バグチェックが難しい
  • 記述が冗長になる

という問題点もあります。

また、Velocityはレンダリング速度が遅いのではないのかという懸念もありましたが、Velocityを採用する際のベンチマークからも十分な速度でレスポンスを返せることが証明されていますし、サービスを運営している中でレンダリング速度が問題になったことはありません。

VelocityとScalaTemplateの比較

今回は、ソーシャルゲームというサービスの特性上、リリースサイクルの短縮が大きな課題となります。

そのため、変更からリリースまでの時間と、デザイナーの作業の行いやすさという点から比較します。

デザインの変更から反映までのステップを下の図に示しますが、これだけでリリースサイクルという観点からはVelocityのメリットが充分に感じられると思います。

まとめ

このように、play-velocity-pluginはデザイナとの協業、リリースサイクルの短縮という点において利点があります。

既にOSSとしてGitHubおよびMavenリポジトリに公開してありますので、Play!Framework2をご利用の方は、ぜひ試してみていただきフィードバックをいただければと思います。

社内だけでなく、皆様のご意見も頂いた上で、今後このplay-velocity-pluginを育てていきたいと思っています。

また、近いうちにplay-velocity-pluginを実際に利用するノウハウなどもこのブログで紹介していきたいと思います。


2012年10月2日

kasuya

DevLOVE関西 2012 Drive にて、乙女ゲームの開発事例をお話させていただきます

ソフトウェア開発部の粕谷(@daiksy)です。

11月10日(土)に、大阪で「DevLOVE関西2012Drive」というイベントが開催されます。

そこで、スピーカーとして「乙女ゲーを支える技術」というセッションを担当させていただくことになりました。

■DevLOVE関西2012Driveとは?

このイベントは、DevLOVEという開発者コミュニティが主催する勉強会です。今年のテーマは”Drive”。

技術とファシリテーションが前輪と後輪となって、開発を駆動していく、というマルチセッション方式の勉強会となっています。

■発表内容

弊社では今年、play2.0 + Scalaという技術を使って数本のソーシャルゲームを開発しました。

わたしも、乙女ゲーの開発に携わり、これらの技術を用いた開発を経験しました。

開発言語として関数型言語が注目される昨今において、この開発事例は多くの方に興味を持って頂き、いくつかの勉強会でお話する機会をいただいています。

play2.0 + Scalaでの開発において、現場でどのようなことがあったのか。

弊社のエンジニアは、それらの技術をどのように身につけ、リリースを迎えたのか。

そういった開発事例をいくつかのエピソードとともにお話しようと思っています。

他にも、大変魅力的なセッションが目白押しなので、是非お越しください。

みなさんとお会いできるのを楽しみにしています。


2011年08月2日

Play frameworkでWebSocketを使う

こんにちは。フリュー モバイル事業部の九岡です。 今回はPlay frameworkのWebSocketサポートをご紹介します。

WebSocket

WebSocketとは、WebブラウザとWebサーバ間でリアルタイムに双方向通信を行うための通信方法です。

ブラウザ-サーバ間のリアルタイムな双方向通信は、これまでもFlashやAppletでソケット通信を使えばできましたが、

  • JavaScriptから直接アクセスできない
  • Webの範囲外であるが故にWebフレームワークが対応しておらず、サーバ側の実装も面倒くさい

という難点がありました。

そこでWebSocketです。
双方向通信の仕様がWebSocketとしてブラウザに取り込まれる事で、実装の敷居が下がりました。

  • 最近流行のHTML5+JavaScriptのクライアントアプリにちょっと手を入れるだけで双方向通信に対応できる
  • 巷のWebフレームワーク等のサポートが始まり、サーバ側の実装コストも低い

Play framework meets WebSocket

さて、本題のPlay frameworkですが、バージョン1.2からWebSocketをサポートしています。
Play frameworkはMVCアーキテクチャに基づいたWebフレームワークですが、
C層にWebSocketControllerというものが追加されました。
これを実装するだけで、既存のWebアプリケーションをさくさくっとWebSocketに対応させることができます。

これまで利用してきたHTTPベースのWebアプリケーションを同居させることができたり、同じModelをWebSocketControllerからも利用することができるなど、相互運用性が高いところもポイントです。

環境

Play framework

バージョン1.2以降

ブラウザ

最近の * Google Chrome * Safari * Mobile Safari Mobile Safariというところがポイントで、実はiPhone/iPad/iPod touchでもWebSocketが使えます。

実装例

双方向通信のイメージが湧くように、WebSocketを利用してWebベースのechoサーバを実装してみます。 仕様は以下のようにします。

  1. テストページから、サーバにメッセージを送信できる
  2. 送信されたメッセージが、サーバからそのまま返送される
  3. 返送されたメッセージがページ内に表示される

View

まず、Viewを作成します。

app/views/Echo/demo.html

id=whatのinput要素にメッセージを入力して、id=sayのボタンをクリックするとメッセージをWebSocket経由でサーバに送信します。 id=messagesのdiv要素に、全メッセージが表示されます。

このコードで@@{ /** */ }となっている部分は、WebSocketの接続先の指定です。
このViewがHTMLとして出力される時には、conf/routseのルート定義に基づいて、

のように置換えられます。

また、

は、sendの引数に渡した文字列をWebSocket経由で送信します。

は、WebSocket経由でサーバからメッセージを受け取る度に実行されます。

がサーバから送信されたメッセージです。

Integration Test

仕様1〜3のテキトウなテストを書きます。

今回はWebSocketというブラウザに依存する機能を通したテストなので、Integration TestをPlayのSeleniumテストケースを使います。

先ほど作成したViewを見ながら、
test/Application.test.htmlに以下の内容を追記してください。

このテストケースでは、

  • /echoを開いた時に、エラーが発生していないこと
  • input要素にメッセージを入力して送信ボタンをクリックすると、しばらく後にメッセージがページ内に表示されること

を検証しています。

2つめのテストでは、メッセージがWebSocketを経由してサーバに送信され、サーバがそれを送り返してページに表示される、ということを期待しています。

作成したテストケースは、

から実行できます。

WebSocketController

テストページの表示と、WebSocketでのメッセージのやり取りのためのコントローラを実装します。

Echo.demoがテストページを表示するメソッドです。
render()により描画されるViewは、先ほど作成したapp/views/Echo/demo.htmlです。

Echo.WebSocketEcho.listenがWebSocket経由のメッセージのやり取りを行うメソッドです。
1コネクションあたり1回呼び出され、接続終了までループでメッセージを待ち続けます。
HTTP用のコントローラとは全く実装方法が違いますね。

routes

conf/routesにHTTP, WebSocket用コントローラへのパスを登録します。

前者はhttp://localhost:9000/echoのようなURLへのHTTPリクエストをEcho.demoへ、
後者はws://localhost:9000/echoのようなURLへのWebSocket接続をEcho.WebSocketEcho.listenへルーティングします。

実行例

では早速実行してみましょう。

ブラウザで以下のURLにアクセスすると、デモページが表示されます。

ポート番号はconf/application.confで設定したものです。


[テストページ]1

テストページ

このように、送信したメッセージがWebSocket経由でechoされてページに表示されます。

また、

から、作成したSeleniumテストケースを実行することができます。


[Seleniumテストの実行例]2

Seleniumテストの実行例

このように、ブラウザのWebSocket実装を通したテストもブラウザ上で実行できます。

まとめ

この記事では、Play frameworkのWebSocketサポートについて説明しました。

  • WebSocketはWebブラウザとWebサーバの双方向通信のための規格です。
  • Play frameworkでは、WebSocketControllerを実装するだけで既存のWebアプリに双方向通信機能を後づけできます。
  • Play frameworkでWebSocketを利用したechoサーバとテストページを実装しました

Play frameworkなら、WebSocketを活用したWebアプリがさくっと作れそうな気がしますね!(ΦωΦ)

ソースコードはコチラ(GitHub)