App Engine + Java 覚書
- Spring 載せない方が良い
- jsp でなく Freemarker 等を使う
- jsp を使うと Jasper が初期化に必要になり、スピンアップが遅くなります (5,600 ms ほど)
- Freemarker などを使うようにすると、Jasper が必要ありませんのでスピンアップが遅くなりません
- Frontend は Go や Python など軽いランタイムで動かすのも手です。API を叩いてデータを取ってくるか、または Remote API を使って直に Datastore を触る事を検討します。
- DatastoreCallbacks を効果的に使う
- Datastore の操作(Put, Get 等)に対して処理をフックすることが出来る機構です
- Memcache から結果を返したり、Put 後に Memcache.put したりなどということが包括的に出来ます
- TaskQueue を効果的に使う
- とんでもなく便利です。
- 本当に軽い処理で、URL Handler を切るまでもなければ、DeferredTask が使えます。クラスを書いて、Queue に突っ込むだけで処理が出来る便利クラスです
- Upload 処理は
BlobstoreService.createUploadUrl()
を使う - Request と Response をログに吐き出しておく
- Max Idle Instances は 1 にしておく
- Instance Size を上げておく
- 標準の 128MB Instance では JVM と必要なクラスをロードするだけで割とかつかつです。
- 基本的に Instance Size は大きければ大きいほど効率よくリクエストが捌けます。
さんこう
PubSub なもうそう
Entity の変更を通知したいことが多々ある。たとえば、「メッセージを受信しました」は、recipient_id
が自分の Message
オブジェクトの追加を検知して、通知を行なっていると言える。
このハンドラーは例えば Rails であれば Model の after_create
などで管理されることになる。
しかし、このような書き方をすると "Entity の管理" と "通知" の責務が一緒くたになってしまう。
分割!分割!と叫ぶわけではないけど、モバイルの Push 通知の台頭で、通知の負う役割範囲が大きくなってきた。 以前までは Email で通知すれば十分だったものが、Email, APNS, GCM と通知サービスの幅が広くなってきている。Web においても、更新された情報を元にページをリアルタイムに更新したい、という欲求が芽生えるときがある。このような場合、WebSocket だとか Polling だとか考えだすと、「通知」と一言で呼ぶものはいかに幅広いものか。 他には、ログやアナリティクスの要望も大きい。通知が失敗したら、再送をしたいという要請だってある。そして A/B テスト…と。 場合によってはユーザの locale によって i18n したりね…
ここまでやることが増えると、分割したほうが良いというのは自然な欲求だと思う。
Entity だって after_create
とか考えなくて、何でも良いから変更をぶん投げたい。
上記の理由を踏まえ、次のようなあーきてくちゃを考えてみた。
- DB サービスは、Entity の変更をすべて PubSub サービスに通知する。
- 通知フィルタサービスは、PubSub からの通知を購読し、通知すべき情報をフィルタリングし、実際に通知を行なう。
- 通知サービスは通知を行なう。
まあ、大仰にサービスを分けなくとも、コードだけでも分けることが出来れば十分。3. は Amazon SNS, SES にぶん投げる事が多いと思います。
A/B テストしたい!とか、管理画面で文言テンプレート変えたい!などとなってくると、流石にサービスとして分割した方が良いでしょう。
というもうそうを垂れ流すだけで記事を終わる ( ´ー`)。о
もっと読む
- Amazon SNS で Push 通知を一斉配信するときのフィルタリングについて考える | Developers.IO : http://dev.classmethod.jp/smartphone/sns-push-filtering/
Riot.js を試食する
いろいろ雑いコードで動きます。jQuery 以上 Vue.js 未満くらいで考えるといいです。 Rails + HTML テンプレートで大部分が動いてて、ちょっとだけ JS を入れちゃうときに使うカンジです。 SPA には向きません。
eval とかも使われていなく、<script>
に書いたものがそのまま function として使われているので、デバッグもそこそこ考慮されてると思います。
肝心の試食コードはこんな感じ
<book> <ul> <li each={books}>{title}</li> </ul> <form onsubmit={add}> <input type="text" name="name"/> <button>Add</button> </form> <script> this.books = []; add(e) { var input = this.name; this.books.push({"title": input.value}); input.value = ""; } </script> </book>
これで <book></book>
と HTML に書いてやると動きます。
riot.js
本体が 3.5KB (minify + gzip) と軽く、フレームワーク自体が重量級な Angular とかを入れたくないときとかに良いと思います。
依存関係とかどうするんだろう?と思ったけど、多分依存も無いような小さいことをするためのフレームワークなんでしょう。 やるならば、browserify で require したり、普通に出力された js と依存ライブラリと一緒に concat してやるとか。
Chrome Push Notification でメッセージに本文を載せる方法
2015/04/28 現在、Chrome (M42) では Push Notification にてメッセージの本文を変える事が出来ません。 これは、現在の仕様では GCM からの payload に任意のデータを載せる事が出来ないためです。したがって、毎回同じメッセージを表示する事になってしまいます。
これでは迷惑な通知にしかなりませんね。対策を探していたら偶然見つかったのでここに書きます。
次のような処理の流れにてメッセージの本文を変えることが出来ます。
- Push Notification を受け取る。
- Service Worker 上の
push
イベントが起こります。
元ネタは Roost という、まんま Chrome, Safari 向けの通知を提供しているスタートアップのコードです。 Roost の Service Worker のソースへのリンクを載っけておくので、上記の処理を実装したい方は各自参考にしてください。
https://dl.dropboxusercontent.com/u/7817937/roost-chrome/roost_worker.js
クローラー開発の知見
- 鍵は「リンク先を辿るか辿らないか」の処理。
- この処理が甘ければ甘いほど、同一サイトのクロールが遅くなります。同じ URL を踏まないようにするため、以前辿った URL を除外したり、パラメータをイイ感じに省いて正規化してあげる必要があります。
- (鍵は「クロール枠の節約」と言い換える事も出来ます)
- 特別なことが無い限りは
?
,#
移行は捨てる - ただし、一部サイトはまだ
?id=hoge
などとやっている - canonical を使った URL 正規化の判断はけっこう面倒くさいです
- 既に辿った URL は KVS に突っ込んでおく。
- 5 万ページ程度のクロールでも 150 万くらいのリンクがあります
- MySQL とかに突っ込むと既に辿ったページを判定する為のクエリがとても重い
- 素直に KVS に突っ込みましょう(AppEngine なら Datastore + Memcache)
- ページをクロールするときはとりあえず丸ごと S3 とかに突っ込んでおく
同一ホストに対しては 1 秒 1 URL のリクエスト、とクロール枠を定めると、一日あたり 86400 ページしか取得出来ません。 URL 正規化をミスると簡単に本来の 10~100 倍ほどのクロール対象 URL が出来てしまうので、URL 正規化をキチンとやらないとクロールが一生終わりません。
リリース後のデバッグのために、ログと DB のダンプを送る機能を付ける
アプリはリリースするととにかくデバッグしにくいです。 Crashlytics を入れておくのは当然としても、これだけでは対応しにくいケースが沢山あります。
たとえば、データの不整合が出たり、予期せずデータが消えてしまったりするケース。 これらのケースは例外が発生しないので、とてもデバッグがしにくい。サポートもしにくい。
という事態が現在進行形なので対策を考えました。
解決策
「ログ」と「DB」のダンプを送れる機能を付ける。
容量の問題。 DB については、件数にもよりますが 1000 件のレコードがあっても ~1MB で済むと思いますので、送れない範囲ではない。 ログについては、1 レコード ~100bytes ほどだと思うので、直近のレコード数で切って送信すれば問題ないでしょう。
とりあえず実装方法を妄想するだけしてみました
ログの保管の仕方(妄想)
iOS の場合、マクロを使って専用のロガーを作って上げると良いと思います。
つまり、NSLog
を直接つかうのではなく適当な Prefix を付けた SOMELog
を使ってログするようにします。
#if DEBUG #define SOMELog(...) NSLog(@"SOME: %@", [NSString stringWithFormat:__VA_ARGS__]); #else #define SOMELog(...) SOMEStoreLog(@"SOME: %@", [NSString stringWithFormat:__VA_ARGS__]); #endif
これで SOMEStoreLog
を実装すれば OK です。中身は適当にバッファしてファイルに保存するだけ。
CocoaLumberjack/CocoaLumberjack · GitHub とかがイイ感じに実装を提供していたと思います。
DB データの取得方法(妄想)
.sqlite
ファイルを丸ごとアップロードすれば良いと思います。
CoreData を使っていれば NSPersistentCoordinator
でファイルパスを指定しているはずなので、こちらを取得してアップロードします。
まとめ
こんな感じでやってみるかもしれないし、良かったらまた書くかも