Author Archives: susumuis

About susumuis

メイドカフェによく居るWebエンジニア

5年間Mayaaを使って思ったこと

関連記事はこちら:そろそろ2年間Mayaa使ってわかったことを書く

上記記事を書いたのが2011年ですので、あれから3年。まだMayaa使ってます(笑)

一つのプロダクトをずっとメンテし続けられるって、ビジネス的に成功した証拠ですから素晴らしいです!というわけで、10年の半分Mayaaを使い続けて思ったことを書いてみようと思います。

Mayaaは長期メンテに耐えるライブラリか

もちろんYesです。もう作られてから10年近い老舗プロダクトで、Javaは1.4準拠だったりしますが、それを加味しても十分現役で使えます。それは、私の勤務先でもずっと使い続けられていることが証拠です。

教育コスト・生産性は

当初とっつきにくい、教育コストがかかると思いましたが、大して問題になりませんでした。既に10人以上の開発者がMayaaを使った開発をしていますが教育コストが高いと思ったことはありません。

この辺りは、「2年間~」のときに書いたように開発ルールを定めたことが効いたのだと思います。はじめにビビってこれらのドキュメントを整えたことがかえって組織には良かったのかもしれません。

コードとデザインが分離するのでフロントのコードがスパゲッティにならないことも良かったです。最初のめんどくささは確実に後から利益となって返済されています。

デザイナーとの相性

あれから、想像した以上にデザイナーと開発者のすれ違いというものを多く目撃してきました。しかしそれはそもそも話せば分かる問題だったり、Javaプログラマーは業務システムを多く経験してきていてフロントに疎いことが問題だったりするためであり、むしろ、Mayaaを使うことで、一緒のプロダクトを同時開発することで発言した必然的なことであるような気がします。

多くの組織では、デザイナーと開発者の線を明確に切るようです。うちのように両者が入り乱れて開発できる現場というのはそんなに多くないでしょう。それができることは、お互いに、そしてプロダクトにとっても、ユーザーにとっても良いことだと思います。

パフォーマンス

今私が全力で取り組んでいる課題です。インフラがクラウド化してパフォーマンスとコストが直結したこと、Webの利用が広がりユーザトラフィックに対してサーバの余裕はもはやないこと、そしてWebブラウザの性能が向上したこともあり、低コストで、高パフォーマンスを実現することがWebアプリケーションに求められています。

そんな中で、Mayaaははっきり言ってパフォーマンスが良くありません。

Mayaaはかなり頑張っています。まず重要なのがキャッシュです。SpecificationCacheというオブジェクトに、ページのツリーデータがキャッシュされていきます。このため、普段のレンダリングは十分に高速です。ただし、キャッシュを行った結果は次の2つの問題点が発生します。

  • キャッシュ自体のメモリのオーバーヘッド
  • 更新直後などのキャッシュがない状態のパフォーマンス

キャッシュ自体のメモリオーバーヘッドは大きいです、たった数10KB程度のテンプレートファイルが1MBを超えたりします。特にJava6の古いマイナーバージョンや、Java5以前を使っているなら、即Java7以降に移行するべきです。Java6u14以降には圧縮ポインタという機能が搭載されています。詳しくはこちらに書きました。→64bit環境でなんかheapを多く消費するなあと思ったら

それでも、メモリを食うことは変わらないので、なるべく大きめにヒープを割り当てるようにしてください。トラフィックやサイトの種類にも依存しますが目安としては全テンプレートファイルの10から50倍程度用意するべきだと思います。

キャッシュがないときの問題も大きいです。そこで、Mayaaは可能な限り、キャッシュがない状態を防ごうとしています。ヒープがいっぱいになってキャッシュがGCによって消えてしまった場合や再起動した時も、ハードディスク上にserializeしておき、そこから復元することでXML解析の時間を短縮しようとする機能があります。この機能はデフォルトでOFFになっていますが、ONにしても問題が発生することはないので、プロジェクトの初期段階からONにすることをおすすめします。

キャッシュがない状態でのレンダリングで最も時間を食っている原因は、Rhinoスクリプトのコンパイル・最適化です。Rhinoスクリプトをそのまま実行すると遅いので、一旦Javaクラスとしてコンパイルするとう最適化を行っています。そして、Javaクラスは繰り返し実行するにつれてJITコンパイルされ次第にネイティブコードに近くなっていきます。なので、数回同じページを表示すると、JSPやServletで画面を構築した場合に劣らないくらい高速になります。VMを再起動したり、テンプレートを更新するとこれらキャッシュは消えてしまいます。コンパイルされたRhinoスクリプトはtransientなので、シリアライズ・デシリアライズされると最適化がはじめからやり直しです。この件については、今後Mayaa自体が効率化することを望みます。

究極のパフォーマンス改善は、そもそもRhinoScriptを使用しないことです。RhinoScriptを使用しないためには、独自のProcessorを作成する必要があります。Mayaaのソースを読んでいくことで、TextCompiledScriptが使用されている箇所を軒並み潰して、Reflectionなどを使用して直接Javaコードを呼び出すようにすることで、ページの処理速度が最大5倍程度速く出来ました。

拡張性

かなり良いです。5年間で本体をforkし独自のコードを挿入する必要があったことはありません。

Processor, InjectionResolver, Builderなどを独自化することで全く別のテンプレートエンジンに仕上げてしまうこともできます。

ただし、一方で、拡張するには非常に多くのコードを書かなければいけなかったり、キャッシュやMayaaツリーの解析など、あらゆる機構が独自で実装されているため、ソースコードのかなりの部分を精読しなければなりません。なんでもできるけど、その難易度は高いといったところです。

XPathMatchesInjectionResolverは無効にするべき

Mayaa User Mailing listに私が報告したとおり、こいつがあるとパフォーマンスが低下します。またメンテ性が悪いのではじめから使うべきではありません。

org.seasar.mayaa.provider.ServiceProvider
を編集し、下記のようにコメントアウトしてしまいましょう。

<!-- <resolver class="org.seasar.mayaa.impl.builder.injection.XPathMatchesInjectionResolver"/> -->

動的なページの構築、MayaaはSPAよりPJAXと相性が良い

最近はSPAといって静的なHTMLページ一枚でWebアプリを構成し、JavaScriptによる非同期リクエストにサーバはJSONを返すことで画面を構築していくというアプローチが普及しつつあります。AnqularJSなどがそのページの作り方をサポートするライブラリとして有力です。

この構成でサイトを作る場合は、そもそもMayaaの出る番がありません。しかし、もし作っているものが、Webサイトの延長で、全ての画面の状態にはURLがあるという状況においてはSPAにせず、Mayaaを使ってPJAXを実装するとスムーズです。

つまり、Ajaxに対してレスポンスするのはJSONではなく、普通のHTMLページであり、jQueryを使うなどしてここから必要な断片を画面に上書きするというアプローチです。Push Stateを使用してURLも書き換えてしまえばSEOもバッチリです!このURLが静的コンテンツとしてMayaaにリクエストした時のページのURLでもあり、静的ページと動的ページの両方をMayaaによって実装することができます。

2014年以降もMayaaは最強か?

これについては、なんとも言えないでしょう。
拡張しやすさを求めるならMixer2、生産性を求めるならThymeleafもあります。パフォーマンスを求めるならJSPで割り切るのも手です。

デフォルト状態でのデザイナーとの相性が非常に良いので、CMSのような物を作りたい場合は、Mayaaは未だにかなり良い選択肢です。この領域にはWordPressというそもそものデファクトスタンダードがあるので、わざわざJavaで作るという機会はあまりないかもしれません。でも、もしそのタイミングが訪れたなら、WordPressのテーマやプラグインよりもずっと良い基盤です。

パフォーマンスの問題がシビアな状況ではあまり適切とはいえません。あらゆる汎用エンジンは何かしらパフォーマンスとトレードオフの関係にあります。いっそ、独自でテンプレートエンジンを作るのも手だと思います。

Mayaaの柔軟性を取り入れつつ、パフォーマンスも良くしたい場合、私が業務で取り組んでいるRhinoを使わないしくみがもし汎用化できて、公開することができれば最高ですね。それは私の業務的にはプライオリティが高いことなので、案外実現してしまう可能性もあります。が、まだ約束できることではないことので、現時点ではまだ考慮にいれられないことが残念です。

Let’s Go! #Golang勉強会 Vol.1に参加してきました

勉強会と Co-Edo について

コワーキングスペース茅場町 Co-Edoで行われたGo言語の勉強会Let's Go! #Golang勉強会 Vol.1に参加してきました。Co-Edoは去年はじめてのgit勉強会&もくもく会 に参加させていただいたのが初めてでその後利用していませんでしたが、オーナーの田中さんににはその後Facebookでつながっていただいていたので、もう一度利用したいと思って思っていました。

今日の勉強会は、コワーキングスペースの片隅机1,2卓を囲んで参加者4,5人の小さな勉強会でした。僕以外の人はGo言語経験者みたいです。僕は今日はじめてインストールしました。

Go言語について

Go言語といえば、Googleが作っている新しいプログラミング言語です。コンパイル式でバイナリを出力できるのが特長で、C++をより開発しやすくしたような立ち位置です。そのため、速度を求められる領域での利用が多く、事例としては、Dockerなどがあります。Gunosyのインフラで使用されていることも有名です。僕のTwitterのTimelineの中でもやっている人をちょくちょく見かけるようになりました。

初めて触ったGolangの印象

半日触ってみた印象としては、今からC++を勉強するよりは学びやすい印象を受けました。文法はCやJavaのようでpythonのようなLLの影響も受けている少し独特な記述の言語ですが、使っていればすぐに馴れそうです。

Webフレームワークもありますが、まだ企業で利用するほどには安定していないようです。

しかしライブラリが充実しているので、その気になれば結構良いWebフレームワークやテンプレートエンジンが作れるんじゃないかなと思います。

作ったプログラム(しょぼいので注意)

ひとまず、今回僕はGoを使って簡単なWebアプリを作ってみましたw

URLのパラメータにfrom, toを指定して、任意の範囲でグラフィカルなFizzBuzzができるアプリです。誰の役にもたちません(笑)

普通にやると

$ go run web.go

のようにして、localhost:4000にアクセスして動作を確認できますが、GCE上のDebian(つまり、このブログを動かしているインスタンス)でテストしていたので、4000ポートにアクセスできません。

curlならできますがつまらないですね。

susumuis@instance-4:~$ curl http://localhost:4000/?from=1&to=10
[1] 27781
susumuis@instance-4:~$ <!DOCTYPE html>
<html>
<head>
<title>hello, golang</title>

<style>
    .fizz { color: red; }
    .buzz { color: blue; }
    .fizzbuzz { size: 1.5rem; color: purple; font-weight: bold; }
</style>
</head>
<body>

to value must be integer
</body></html>

なので、一時的にこのブログを配信しているnginxに曲がりをしてインターネットに配信しちゃいましたwww「おまえ、いきなり本番環境に反映する気か!」などのツッコミには屈しません。(もう落としてます)

    location /goweb/ {
        proxy_pass   http://127.0.0.1:4000;
    }

golang - web - fizzbuzz

一応jQueryも入っているのでデバッガで画面いじるのが醍醐味ですw

Goの使いどころ

まだ事例は少ないですが、静的型付け・コンパイルというのがGoの最大の特長でしょう。それでいてpythonなどのライトウェイト言語の特長を取り込んでいるので、非常に良い言語だと思いました。次のような事例では有効でしょう。

  • インフラなどのリソースを極限まで活用したい
  • go言語がインストールされていない環境にアプリケーションを配布したい

ちなみに、

$ go build web.go

のようにしたらバイナリを出力できます。出力されたバイナリはgoがインストールされていない環境でも動作するようです。

susumuis@instance-4:~/godev$ go build web.go
susumuis@instance-4:~/godev$ ls -la | grep web
-rwxr-xr-x 1 susumuis susumuis 5703384  7月 27 21:42 web
-rw-r--r-- 1 susumuis susumuis    1466  7月 27 17:43 web.go

1.4KBのソースコードから吐き出されたバイナリは5.5MBですかwライブラリを含んでるのか大きいですね。フロッピーに入らない。

GoはWebがまだ弱いので、実質的に、静的型付けが欲しいWeb言語という用途ではまだでしょう。その用途ではFacebookが開発したHackが良いと思います。HackはPHPに型を載せたようなシロモノなので、普通にPHPのエコシステムが使えます。パフォーマンスはFacebookがHHVMを開発して頑張っています。さらに極限までチューニングしたい場合は素直にJavaを使うのが現状一番良いでしょう。ただし、Javaを使えば一律に速くなるというわけではなく、JVMという怪獣を手懐ける必要があります。

僕はその必要性を感じませんが、「サーバーもフロントも同じ言語を使いたい」という場合は、現状はNode一択なのでしょう。Dartがどこまで来るのか興味が湧いています。

実はこのブログを動かしているWordPressにHackで書いた独自プラグインを載せています。そんなことも簡単にできちゃいますが、それについては後日書きます。

本番環境でWordPress Themeを安全に開発できる環境を作りました

テスト環境なしの開発はさすがにない

WordPressに移行したこのブログも大分システム・データが安定してきたので、そろそろフロントを調整していくフェーズに入ろうと思います。

ところでWordPressって「外観 > テーマ編集」から直接テーマのphpファイルエディットで来たりするんですが、さすがに怖いっすww

バグったら画面真っ白ですし、下手なセキュリティホール仕込んでしまうかもしれない。当然開発は別の環境でやって、テスト済みのモジュールを本番に反映するっていうのがソフトウェア工学上の基本セオリーです。

そのために自前のMacにもWordPressインストールして、開発環境作ってもいいんですが、それって古いっす!今どきFTPとか情けない。今日パソコンってのはシンクライアントであるべきです。明日このMacが壊れてWindowsに買い替えたとしても、すぐに開発が始められるのが理想です。

概念図

ということで、下記のような環境を作りました。

my-wp-env

データ保全上は完璧ではありませんが、僕がミスっても大丈夫にするためなんで費用対効果からこんな感じにしました。

簡単な解説

Ephemeral DiskとPermenet Disk

Google Compute Engineでサーバを作ると標準で割り当てられるEphemeral Diskは揮発性が高いディスクで、インスタンスがダウンすると消えてしまいます。そこで、データなどは、データ保全性が高いPermanent Diskを追加しこちらに格納することがセオリーです。それぞれ
/
/mnt/pd0
にマウントし、Ephemeral Diskにnginx, hhvm, maria-dbなどのシステムを入れ、pd0にwordpressやmysqlのデータを配置しました。

あとの、細かい説明は割愛w

テーマの編集手順

新しいテーマを作成しようとした場合はこのようにする想定です。

まず、/mnt/pd0/themes/に新しいテーマディレクトリを作成します。おそらく元となるテーマがあるのでコピーして作成します。

$ cp -r twentytwelve susumuis-1

次に、/mnt/pd0/wp-content/themesにシンボリックリンクを作成します

$ ln -s /mnt/pd0/themes/susumuis-1 /mnt/pd0/wp-content/themes/testing

これで準備完了です。ちなみに、/mnt/pd0/wp-content/themes/には実際にテーマディレクトリは配置せず、必ず/mnt/pd0/themes/に実ファイルがあり、/mnt/pd0/wp-content/themes/は常に次のシンボリックリンクを配置します。

production
testing

あとは、productionやtestingの向き先を変えることで、現在のバージョンのテーマを切り替えていくこととします。

テスト環境へのアクセス

標準状態のWordPressでは、テーマは一度に一つしか選択できないので、このままでは本番をproductionにしつつ、testingテーマへアクセスすることができません。

そこで、不本意ですが、プラグインの力を借ります。

Theme Test Drive

というプラグインを使います。このプラグインをインストールすれば、ログイン中の管理者だけ、あるいはURLに?theme=xxx(xxxはテーマ名)を付与したときだけ、別のテーマで表示することができます。

ここで、theme=xxxという指定はプラグインのオリジナルのままでは、一般ユーザーも指定できてしまいます。これでは、開発中の画面を一般ユーザーに晒してしまい、運悪くバグがあると、データを壊すなどの危険があります。

そこで、プラグインを一箇所書き換えました。

theme-test-drive/themedrive.php

@@ -192,6 +192,9 @@

   function themedrive_determine_theme()
   {
+      if (!current_user_can(themedrive_get_level())) {
+          return false;
+      }
       if (!isset($_GET['theme'])) {
           if (!current_user_can(themedrive_get_level())) {
               // not admin

(patchコマンドでパッチできるかはテストしていません)

これで、管理者としてログイン中の時だけ、testingのテーマを試すことができ、一般ユーザーは?theme=xxxと指定しても、テーマを変更することができなくなりました。

テーマの編集をバージョン管理

cd susumuis-1        
git init
git add .
git commit -m 'first commit'

これでとりあえず、バージョン管理できるようにしました。間違えてしまった時はresetで戻ることができます。ある程度できたらcommitしてテストをすることで、「あれ、さっきまではできてたのに」というのを防ぐことができます。必要に応じてリモートリポジトリを作成したりブランチを切ったりしていきたいと思います。

公開

テーマが完成して本番に公開するときはシンボリックリンクを貼り直すだけです。