GroovyServとGradle Daemonとの違い
10/23 9:50 訂正&追記: GradleがデフォルトでDaemon起動すると書いてた件 (thanks to きょんくん)
元記事: http://dev.classmethod.jp/server-side/gradle/gradle-first-contact/
経緯的な
id:daisuke-mが昼過ぎに技術を語るいつものシリーズにGradleを追加した旨のツイートをしてました。 生粋のGradlerとしては喜び勇んでチェックしてみたんですが、「Gradleのインストール」であるべきところを「Groovyのインストール」と書いてたので、親切心でコメントしたわけです。
@daisuke_m "Groovyのインストール"->"Gradleの〜"
— Yasuharu Nakano (@nobeans) 2013, 10月 22
そこまでは良かったのですが、ちょっと調子に乗ってしまいまして、
@daisuke_m つ "ついでにせっかくなのでGroovyもインストールしておきましょう。`gvm install groovy`"
— Yasuharu Nakano (@nobeans) 2013, 10月 22
@daisuke_m 要るかどうかなんて関係ないのです。そこにgvmがあるから入れるのです。groovyを入れたらgroovyservも一緒に入れるのです。
— Yasuharu Nakano (@nobeans) 2013, 10月 22
と書いたところ、記事に晒し上げされてしまいました。 なんということでしょう。
そのままフェードアウトしてくれれば終わればまだ良かったのですが、自称怖くないモンスターの人に見付かって問い詰められているid:daisuke-mが忍びなさMAXでつい反応してしまったら、気がついたら私がGroovyServとGradle Daemonの違いについてなんか書かなければならない流れになっておりました。
ああ、恐ろしきかな因果の流れ。
GroovyServとは
Groovyコマンドを実行可能なJVMプロセスをサーバとして常駐させておいて、ネイティブコマンドを使ってTCP経由でコマンド引数をサーバに送り込み、評価した結果を再びTCP経由でもらって出力する、という拙作のツールです。
Groovyに限らずJVM系言語でスクリプトを書いた場合、JVMプロセスを起動して色々なクラスローディングする準備処理が遅くて、1秒ぐらいの待ち時間が発生します。
$ time groovy -e "println 'Hello'" Hello real 0m1.347s user 0m1.481s sys 0m0.168s
GroovyServをインストールして、クライアントコマンド経由で実行すると
$ time groovyclient -e "println 'Hello'" Hello real 0m0.057s user 0m0.002s sys 0m0.005s
すごく...はやいです...。
といっても、初回はサーバプロセスを起動するための待ち時間が数秒かかるんですけども。 一度起動したらそれ以降は上のような感じです。 はいはい、爆速爆速。
なお、mainクラスのエントリポイントを指定できればいいので、JythonとかClojureも同じように高速化できます。 Scalaのコンパイルも頑張れば早くなるようです。 Vim+QuickRunでgroovyConsole風にさくっと実行できるのにフル機能エディタ!みたいなこともできますし、SublimeText2からも似たようなことができます。
一応このネタで2013/5にGr8conf EU 2013というデンマーク コペンハーゲンのGroovy系カンファレンスで発表してきたりしました。
詳しくは、ちょっと雑多な感じですがユーザガイド(日本語)をどうぞ。
Gradle Daemonとは
Gradleも同様にJVM上で動作するツールなので、起動の待ち時間が大変遅いわけです。 毎回JVMプロセスを一から起動してると、さくさくビルドやテストを試せません。 GroovyServみたいな仕組みが欲しいですよね。
ということで、Gradle本体が同じような仕組みを用意したのがGradle Daemonです。 歴史的に言うと、GroovyServの方が若干早いはず。 パクられた!とは言いませんが、時期的なものを考えると多少は参考にされたんじゃないかと自意識過剰気味にアピールしておきます。どうかな。違うかも。
最近のGradleはデフォルトでDaemonを利用します。
GradleがデフォルトでDaemon起動すると書いたな、アレは噓だ。
--daemon
オプションが必要です。
以下はしれっと書き換えてあります。
すいません。
--daemon
オプションはまだデーモンがいなければ起動して使い、すでに居ればそれを使うというオプションです。
というわけで、適当なGradleプロジェクトで、2回gradle --daemon tasks
を実行してみると、2回目が劇的に早くなってるんじゃないかと思います。
たとえば、自宅のかなり古いMacBook mid 2008で試してみると、
$ gradle --daemon tasks ... Total time: 12.02 secs $ gradle --daemon tasks ... Total time: 2.52 secs
とこんな感じです。すごいですね。 jpsしてみると
$ jps 9376 Jps 4136 GradleDaemon
とデーモンプロセスが常駐してるのが分かるはず。
あと--daemon
オプションが付けられていようが、--no-daemon
というオプションを追加すれば、デーモンは無視して自前プロセスで実行できたりします。
デーモンプロセスありきでalias設定してあっても、一時的にはずせるわけですね。
また、デーモンプロセスが要らなくなったら、普通にkill
コマンドでもいいですが--stop
オプションを使えばgradleコマンド経由でコントロールできます。
個人的にはGradle Wrapperを優先しつつDaemonは必須で使いたいので、以下のようなaliasをシェルで設定して使っています。
gradle='if [ -x ./gradlew ]; then GRADLE_BIN=./gradlew; else GRADLE_BIN=\gradle; fi; $GRADLE_BIN --daemon'
こちらも詳しくはユーザガイド(日本語)をどうぞ。
ちなみにGroovyServもビルドにGradleを使っていて、Daemon様におかれましては大変お世話になっております。
GroovyServとGradle Daemonの違い
Gradle Daemonは、Gradleタスクの起動処理をショートカットする GroovyServは、Groovyスクリプトの起動処理をショートカットする
Gradle Daemonは、gradleコマンドのオプションですべて操作する GroovyServは、groovyclientコマンドのオプションで操作するほかに、サーバプロセス起動用のgroovyserverコマンドがある
Gradle Daemonは、ユーザの気付かないうちにデーモンプロセスが常駐してる GroovyServは、サーバプロセスの起動時にコンソール出力してアピールする
気の利いた大喜利風にオチがつけばいいのですが、実力的に無理でした。ごめんなさい。
結局、コンセプト的なものは一緒ですけど、用途が違うので細かいところで色々と違いますね。 あんまりまじめに比べるモノじゃないと思われます。 むしろGroovyServと比べるべきはNailgunとかDripあたり。
おまけ: NailgunとGroovyServの違い
Nailgunはよく知られたJVMプロセス常駐系アーキテクチャのツールです。 歴史的にはGroovyServよりも古いです。 古すぎて2005年以降はまったく更新されていません。 と思ったら、2012/11月にv0.9.1がリリースされててちょっと驚きました。
NailgunはピュアJava実装なのでビルトイン目的によく使われます。 というかAPI的にもそれっぽい感じ。 JRubyもNailgunを使ってますね。
GroovyServはGroovyで実装してるのでビルトイン目的には向いてません。
GroovyServは今更ピュアJava路線に変更するのは不可能ではないにしろ面倒つらい感じです。
ビルトインの代わりに、単にクライアントの引数でmainクラスのエントリポイントを指定するだけで色々なJVM言語の起動時間短縮を計れます。物は言いようです。
機能的な違いでいえば、下のような感じです。
- GroovyServのみサポート
- 両方ともサポート
- System#exit()が実行されてもJVMは落ちない
ほらこうやってみるとGroovyServ結構がんばってるでしょ? Nailgun側のメリットはビルトイン以外に思いつきませんが、他に何かあるかもしれません。 フェアじゃないのは単に知識不足です。すいません。
おまけ: DripとGroovyServの違い
Dripはシンプルで割り切ったアーキテクチャで中々興味深いところはありますが、アーキテクチャやサポート機能が違いすぎてGroovyServとはあまり競合しないふいんき。
簡単に言うと、
http://b.hatena.ne.jp/nobeans/20130904#bookmark-109556574
GroovyServのようなJVMプロセス常駐系/興味深いアプローチ/単一プロセスを使い回すとdirtyになっていくのでJVM引数,クラスパス,起動クラスのハッシュごとに異なるJVMデーモンを使う/コードベースが超小さい/bash限定は楽そう
という感じです。
Dripは、bashを前提にしているので色々実装がシンプルです。うらやましいです。 GroovyServは、Mac/Linux/Windowsで動作します。 現状ではWindowsサポートが若干手薄な気もしますが、次期バージョンではできるだけ環境ごとの機能差がないように頑張っています。
Dripは、カレントディレクトリ毎に別プロセスを起動します。 よって、複数プロセスが常駐します(一定時間でタイムアウトして消える)。 既存JVMプロセスに対するネイティブレベルでのCWD制御とかを不要にしたりしてます。割り切り方が素敵です。 GroovyServは、ポート毎に1つのサーバプロセスを用意しています。 普通はデフォルトポートで十分なので、サーバプロセスは1つだと思えばOKです。ある意味シンプル。 CWDなどは毎回力業で反映しています。
こんな感じでしょうか。
測定マシンについて一言
なお、上記記事中の性能的なものはすべて自宅の超古いMacbook 2008 Midで測定してます。 そうとう遅いです。 苦痛です。 今晩のAppleのイベントで新MBPでたら速攻で買い換えたい所存です。
まとめ
というわけで、GVMを使ってGradleをインストールしたら、ついでに
$ gvm install groovy $ gvm install groovyserv
すると良いと思います。 更に手が滑って
$ gvm install gaiden
とGaidenのインストールまで進んだあなたはファッションリーダとしての自覚と風格が感じられて頼もしい限りです。
標準バンドルのVisualVMのウィンドウが空っぽになってしまったらキャッシュを削除すべし
経緯的な
9月にTwitterにぼやいた件のその後。
7u40のretina対応のおかげでjvisualvmもくっきりきれいになってるなー
— Yasuharu Nakano (@nobeans) 2013, 9月 12
と思って、7u25と比較したりしてたら、メインウィンドウに何も表示されなくなってしまった...
— Yasuharu Nakano (@nobeans) 2013, 9月 12
7u40のjvisualvmが使い物にならなくなってしまった。再起動したら治るかな...
— Yasuharu Nakano (@nobeans) 2013, 9月 12
@cero_t 最初は普通に起動したんですけど、retinaとそうじゃないのを比較するために7u25に切り替えてjvisalvmを起動して、その後7u40のGUIをいじったら固まってしまい、その後は起動し直してもウィンドウ前面がグレイ状態(ビューがなし)という状況です
— Yasuharu Nakano (@nobeans) 2013, 9月 12
起動中にjvmを切り替えたせいかもしれないけど、アプリだけ再起動してもずっと同じ状態。7u25に切り替えて起動するとそっちは普通に使える。謎。OS再起動はまだしてない。
— Yasuharu Nakano (@nobeans) 2013, 9月 12
事象
事象としては、jvisualvmを起動するとこんな画面になって何もできないというもの。
Netbeansプラットフォームアプリケーションの素のウィンドウだけ表示してみました的な感じで、メニューバーも本来のVisualVMとは違ってまっさらな感じ。 かろうじてVisualVM臭がするのはAboutダイアログぐらいです。 ホントになにもできません。
http://visualvm.java.net/download.html からダウンロードしたApp版のVisualVMは普通に動きます。
ちなみに、Macです。他のOSで発生するかは知りません。
解決策
VisualVMのキャッシュが壊れていたようです。
/Users/me/Library/Caches/VisualVM/7u14
を削除してから起動したら、正常に動作しました。
ちなみに、7u40や7u45にバンドルされてるjvisualvmを使っても、ディレクトリ名は7u14
でした。
なんででしょうね。
本家Grailsユーザガイドを開くと自動的に日本語翻訳にリダイレクトするChrome拡張
Grailsユーザガイド日本語翻訳版とブックマークレットの紹介 - 豆無日記で、紹介したブックマークレットを毎回実行するのがダルすぎるので、Chrome拡張にしてみました。
以下のURLからChrome拡張をインストールしてください。
セキュリティの都合から野良サイトからの直接インストールはできないようなので*1、いったんダウンロードしてから拡張の管理画面にドラッグ&ドロップするとよいでしょう。
これで、http://grails.org/doc/latest/*
なサイトを開くと自動的にhttp://grails.jp/doc/latest/*
にリダイレクトされるようになります。
これでGoogle検索結果からのリンクでもさくっと日本語版が表示できますね。
コードという程のモノではない何かはGitHubにサラしてあります。
Grailsユーザガイド日本語翻訳版とブックマークレットの紹介
有志によるGrailsユーザガイドの日本語翻訳作業がずっと行われていたのですが、実は8月の頭にちょうど一区切り良い感じになりました。 そろそろリリースされそうなGrails 2.3向けに追記された部分などまだちょっと英語部分も残っていますが、基本機能についてはほぼすべて翻訳完了な感じです。
残念なことに検索しても出てくるのは本家の英語版だけだったりすることが多いです。
URLのドメイン部分がgrails.org
→grails.jp
と違うだけなので、ブラウザのアドレスバーを手作業で書き換えて日本語版を表示させたりして過ごしていますが、さすがに面倒になってきたのでブックマークレットを書いてみました。
- タイトル例
- Grailsユーザガイドを日本語化する
- URL
javascript:location.href=location.href.replace(/grails.org/,'grails.jp')
そのまんまですが、わりと便利な感じなのでよかったらどうぞ。
iPhotoライブラリをDropboxで管理するときに気をつけるべきたったひとつのこと
iPhoto Libraryの中にあるOriginal、Data、Modifiedというエイリアスを削除してから共有しましょう。
なんで?
Dropboxはエイリアス経由のパスであっても気にせずに物理的にサーバ上にファイルを格納するので、画像が多重にアップロードされてしまい、サーバ利用量がヤバいことになります。というか、なった。
具体的にはどうする?
iPhoto Libraryを右クリックのアレで開いて、Original、Data、Modifiedというエイリアスを削除しましょう。Data.noindexというのがあったらそれも*1。iPhotoはこれらがなくても普通に動作しますが、万が一何かあっても自己責任でお願いします。
経緯
元々20GBも使ってないときに、40GBくらいのiPhoto Libraryを共有したら、アップロードに2〜3日かかったあと、合計で105GB使ってますよという状態になってしまったので、有料会員の特権(?)でサポートに問い合わせてみたら、FAQらしくて手順書ページを紹介されました。 対処後に一晩放っておいたら、無事に60GBぐらいに落ち着いて、サイズ保存則が無事に成立しました。
*1:自分はなかった
Gradle1.6の新機能mustRunAfterでタスク間の実行順序が制御可能になった #gradle
Gradleではタスク間の依存関係の指定ができます。
build.gradle:
task a << { println "A" } task b(dependsOn: "a") << { println "B" } task c(dependsOn:"a") << { println "C" } task d(dependsOn: ["b", "c"]) << { println "D" }
実行結果:
$ gradle d :a A :b B :c C :d D BUILD SUCCESSFUL Total time: 1.612 secs
実はこの例のBとCの間の関係は不定であり、実装に基づいて(この場合はアルファベット順だったりしたはず)実行順序が決められます。 ここではA→B→C→Dになっていますね。
しかし、このようなグラフ的には一見どっちがさきに実行されても良いようにみえるBとCの間に、実は微妙な前後関係があって、Cの方が先に実行されて欲しい!というケースは意外とありますよね。
長い間Gradleユーザから臨まれていたこの機能がついに1.6で実装されました(ということをさっき知った)!
Task#mustRunAfter()
の使い方
以下のように使います。
build.gradle:
task a << { println "A" } task b(dependsOn: "a"/*, mustRunAfter: "c" -- 残念ながら今のところここには書けないらしい*/) { mustRunAfter "c" // メソッド呼出として書く doLast { println "B" } } task c(dependsOn:"a") << { println "C" } task d(dependsOn: ["b", "c"]) << { println "D" }
と書くと、こうなります:
$ gradle d :a A :c C :b B :d D BUILD SUCCESSFUL Total time: 1.64 secs
dependsOn
との違い
実は単にA→C→B→Dとしたいだけであれば、↓と書くだけでもOKです。
task a << { println "A" } task b(dependsOn: ["a", "c"]) << { println "B" } task c(dependsOn:"a") << { println "C" } task d(dependsOn: ["b", "c"]) << { println "D" }
mustRunAfter
との違いは、タスクbを実行してみると見えてきます。
一つ前のサンプルだと:
$ gradle b :a A :c C :b B BUILD SUCCESSFUL Total time: 0.723 secs
となります。 bの依存先にcが入ってるので実行タスクのグラフにcが登場するのですね。
さて、mustRunAfter
を使った先ほどと同じこの設定で実行してみると...
task a << { println "A" } task b(dependsOn: "a") { mustRunAfter "c" doLast { println "B" } } task c(dependsOn:"a") << { println "C" } task d(dependsOn: ["b", "c"]) << { println "D" }
実行結果:
$ gradle b :a A :b B BUILD SUCCESSFUL Total time: 1.745 secs
となります。
mustRunAfter
は依存関係の指定ではなく、実行タスクのグラフ上にそのタスクが存在した場合に単にその順序関係を指定しているだけなので、この場合にはタスクcは登場しないのです!
実際どういう場面で使うの?
ふもさんのサンプルがわかりやすそうです。
task clean << { println 'cleaning.' } task build << { println 'building.' } build.doLast { println 'finish!' } task local(dependsOn: [clean, build]) << { println 'local debug.' }
これを実行するとこうなります:
$ gradle local :build building. finish! :clean cleaning. :local local debug. BUILD SUCCESSFUL Total time: 1.638 secs
build
してからclean
するとかso crazyですね。ちょっと何言ってるか分からないです。
ここで実行順序制御にdependsOn
を使おうとすると、
task clean << { println 'cleaning.' } task build(dependsOn: 'clean') << { println 'building.' } build.doLast { println 'finish!' } task local(dependsOn: [clean, build]) << { println 'local debug.' }
実行結果:
$ gradle local :clean cleaning. :build building. finish! :local local debug. BUILD SUCCESSFUL Total time: 1.605 secs
うまくいった!?ようにみえますが、clean
せずに差分ビルドさせようとbuild
タスクを指定すると...
$ gradle build :clean cleaning. :build building. finish! BUILD SUCCESSFUL Total time: 0.755 secs
clean
まで発動してしまいました!!これでは毎回フルビルドになってしまい、開発速度が上がりません。
そこで、mustRunAfter
の出番です。
build.gradle:
task clean << { println 'cleaning.' } task build { mustRunAfter 'clean' doLast { println 'building.' } } build.doLast { println 'finish!' } task local(dependsOn: [clean, build]) << { println 'local debug.' }
実行結果:
$ gradle local :clean cleaning. :build building. finish! :local local debug. BUILD SUCCESSFUL Total time: 0.722 secs
$ gradle build :build building. finish! BUILD SUCCESSFUL Total time: 1.58 secs
素晴らしい!期待通りの結果です。
というわけで、依存関係のdependsOn
と、実行順序の微調整のmustRunAfter
をうまく使い分けましょう。
コペンハーゲンのGR8Conf EU 2013でGroovyServとImproxプラグインを紹介してきた
5/22-24にデンマークはコペンハーゲンで開催されたGR8Conf EU 2013というGroovy系のカンファレンスで、GroovyServとImproxプラグインを紹介してきました。
右の写真はコペンハーゲンの定番がっかり観光スポットの「人魚の像」ですね。 がっかり感が軽減されている一枚を選びました。 まあ、どこの観光地にもがっかりスポットはつきものです。 札幌の時計台にくらべれば人魚はだいぶ良かったんじゃないでしょうか。
GR8Conf EUは今年で5周年だそうです。コペンハーゲンがオリジナルですが、「GR8Conf」ブランドのカンファレンスは別ロケーションでも何度か開かれていて、USは今年も7月にありますしほぼレギュラー化してるようですね。他に覚えているところではオーストラリアでも開催されてました。日本でも開きたいですね。
さて、自分の発表ですが、本日動画が公開され(てしまっ)たので、スライドと動画のリンクを張っておきます。 つたない英語ですがよければご笑覧ください。
- 自分の発表のポータルページ的なもの
- スライド
- 動画
当方の深刻な英語力不足にともない今回は不本意ながら「最初に言い訳」メソッドで乗り切りましたが、やっぱりせっかくやるからには「ネイティブ&インタラクティブ」方式を目指したいですね。 最後、質疑がなくて寂しい感じにみえますが、たぶんみなさん気を遣ってくれた感じで、セッション後に数名の方と技術的な話もしましたからね? *1
他の錚々たるスピーカのみなさんの動画も随時公開されていくはずなので、興味がある方はTwitterで@gr8confをフォローしておくと良いと思います。
そうそう、今度の6/21のJGGUG主催GワークショップZで、G*ワークショップZ Jun 2013 - GR8Conf報告&ライブコーディング&GroovyServ/Improxをやります。 ご興味があれば是非是非ご参加ください。
*1:どちらかというと「お前の英語は悪くない」「問題ないよ」「グレートだったぞ」みたいな慰めにきてくれた人の方が多かったですけど。