ElasticMQでAmazon SQSのローカルテスト環境を作る

以前DynamoDBをLocalで建てるようにしたことでDynamoDBを使ったサービスの開発やテストがローカルで完結できるようになって幸せをかみしめていたのですが、このたびSQSも使うことになり、さてどうしたものかと思っていたらElasticMQというSQS互換インターフェイスを持った実装が存在することを知って、使ってみました。なお自分はElasticってつくとなんでもAWSのサービスみたいに感じてますが、ElasticMQElasticSearchはAWSのサービスではないですね。

ElasticMQはJavaで実装されてるので、ダウンロードしてきて実行すればいいだけなので超簡単。開発も今のところ継続的に行われており、ロングポーリングもver 0.7から対応しているようで素敵です。

Pythonでbotoを使ってキューを登録してみます。

アクセスキーとシークレットはダミーですので何を指定してもいいっぽいです。 続いてキューを読み出します。

読み出しのループを回しておいてキューにデータを入れると、読み出されているのがわかります。

ロングポーリングが正しく動いていない気がするので、今後調査の方向で。

Safari Readerのコンテンツ抽出処理を調べる

Safari(とiOSのMobile Safari)にはReader機能というのがあって、ブログなどでコンテンツ部分だけを抜き出して表示してくれます。iOSにはあるのは知っていて、PC向けのページを読みやすくしてくれて便利なのでたまに活用していたのですが、PC版でもあるんですね。似た機能はPocketReadabilityにもあります。

でもこのリーダー機能、ボタンが出る時と出ない時があります。まあコンテンツ抽出ができない時は出ないんだろうなっていう推測はできるのですが、どのようにコンテンツ抽出しているのかなと。PerlのモジュールでHTML::ExtractContentというのがあるのですが、似たようなことやっているんだろうなって思っていましたが、しらべるとh1~h6の含まれるブロック要素で文字数が多いものが取られているっぽいとかブロックのサイズが云々とか色々観測結果が書かれていました。

しかし更に調べていくと、新事実が。Safari Reader Source Codeによれば、Safari ReaderはJavaScriptで実装されており、以下の手順を踏めばコードが読める。なんだってー。

  • Safariを一旦終了してブランクページを起動する
  • Readerが有効になるページを開く(ページ内のJavaScript次第でうまくいくページと行かないページが有りましたが、ここで例示されているページではうまくいきました)
  • ページがロードされ終わるのを待ち、WebKit Inspectorを開き、停止ボタンを一度だけ押す
  • リーダーボタンを押す

safari reader source Safari Readerのコンテンツ抽出処理を調べる

おお、よめた!

ということで、JavaScriptで書かれたコードを読むことができます。中を読むと要素のIDやClassに”article”、”body”、”content”、”entry”とか入っていたら可能性が高く、”comment”とか”advertisement”、”breadcrumb”、”disqus”とか入っていたら可能性が低いとか、縦横の表示サイズやエリア面積などなど様々なチェックを行っているようです。

DynamoDB Localでローカル開発環境構築

開発環境をローカルで構築すると、ネットワークのない環境でも開発ができるので非常に便利です。特に昨年は飛行機に乗る機会がとても多く、僕の乗っていた便はまだ機内インターネット接続サービスの恩恵は受けられない便ばかりだったので、飛行機の中でも開発を続けるためには、開発環境とドキュメントをローカルに入れておくのはとても重要でした。VagrantDashにはとてもお世話になりました。

さて最近DynamoDBを使ってサービス作っています。AWSのサービスは非常に便利でよいですが、DynamoDBはElastiCacheやRDSみたいにローカルにMemcachedやRedis、MySQL、PostgreSQLなど入れておけば代用できるわけでもないので、どうすんだろ、と思いました。

もう1点DynamoDBの問題として、DynamoDBの課金体系は保存容量以外に毎秒何回読み込みと書き込みができるかに対してお金を支払いますが、無料枠は、合計書き込み5回/秒、読み込み10回/秒までです。でもDynamoDBではグローバルセカンダリインデックスを用意すると、それぞれにつき少なくとも書き込み、読み込み1ずつ割り当てなければならないので、1つのテーブルが1回/秒で済まないケースもあって、本当にお試しでもない限り、無料枠をすぐ使い切っちゃいます。といっても、月数百円とかですむのですが、油断してて料金発生するのも怖い。

で、DynamoDBの互換のシステムをローカルで構築できる物があればいいのになって思って調べたら、Amazonが公式にDynamoDB Localなるものを公開していて、これを使ったらものすごく簡単にそれができてしまいました。

紹介記事は2013年9月ですがリンクは最新版を指すようになっていてダウンロードされたのは2014年1月8日版でした。2013年12月に発表されたグローバルセカンダリインデックスにも対応。Javaで書かれているのでjarファイルを実行すればサービスが起動してしまう。ポート番号はデフォルトが8000ですが、8000なんてなにか他のものと容易に衝突しそうなんでそこはは変えました。

それで、次はアクセスをしてみるのですが、開発はPythonを使うことが多いのでbotoを使いました。botoってconnectionの際にhostとportを指定できるようになってるんですね。

実際のDynamoDBのようにコンソールが用意されていてテーブル作成できるわけではありませんが、そもそもきちんとテーブル定義はコードとして残しておいたほうがいいですね。

AWSのブログ記事に以下の様なことが書かれています。

  • スループットの指定は無視される。スループット以上にアクセスすることはできてしまう。
  • アクセスキーとリージョンはDBファイル名を生成するときにだけ関係ある
  • シークレットキーは無視される
  • テスト用なので、プロダクション作らないほうがいいよ

ということで、これでとりあえずお金の心配も、ネットワークの心配もせずにDynamoDBで開発ができるようになりました。

画像で最も使われている色を抽出するBBColorPickerの処理を読む

自分はサイトやアプリを作る際に色を選択するのが非常にセンスがなくて下手っぴなので何とかせねばならないと思い、理屈から勉強するしか無いという思いに至っています。

で、AirbnbのBlogGuest Experience on iOS7という記事がありまして、友達のウィッシュリストのヘッダ部分の背景色を画像で使われている色から計算するという処理を入れているとのことで、そのライブラリであるBBColorPickerが公開されているので、中身を読んでみました。

このライブラリはBBColorSamplerManagerというシングルトンのクラスが提供されていて、その中では2つのメソッドが提供されていています。1つはcomputePrimaryColorForImageでもう一つはsortColorForImage。

名前からしてcomputePrimaryColorForImageは実際にアプリで使ってるヘッダ部分の背景色を計算するメソッド、2個めは使われている色をソートして抜き出すメソッドって感じに見えます。ブログ中での言及されているように非同期で動作するので、引数として完了時のコールバック関数を指定するようになっています。しかしsortColorForImageのコールバック関数の引数はUIColorの配列かとおもいきや、UIImageだったのでちょっと動作がわかりません。

それぞれのコードを読んでみると2つのメソッドは激しくコピペ臭がするのですが、とりあえず必要な処理はcomputePrimaryColorForImageっぽいのでこちらを読むことに。

ここでまずは画像サイズをちっこくしています。_sampleSizeはinitで32×32に指定されているので、それに合わせて縦横比をキープしながらサイズを小さくして、CGContextに描画してます。

これで縦横最大32ピクセルの画像になりました。

そしてこれを今度は1ピクセルずつループで回して分析しています。

解析にあたってRGBの色をYUV色空間に変換しています。YUVは(Wikipediaによれば)”輝度信号Yと、2つの色差信号を使って表現される色空間”です。結果はy、u、vのそれぞれの変数に入ります。

そしてもう一つ、 RGBの最大最小を取って差分を最大値で割ってますが、これはHSV色空間の彩度(Saturation)を計算しています。結果はsに入ります。

そしてこれらの結果から、その色を採用するかどうかを決めています。

これによると白黒に近い色を除くことで、より画像の特徴を表してそうな色を取り出すようにしているという感じでしょうか。この数値はすごくマジックナンバーな感じですが、どうやって決めたんでしょうね。いろいろ試してチューニングしたのかな。

  • 輝度が高過ぎるものや低すぎる(0.3以下、あるいは0.9以上)の色を除外
  • UVで中心に寄りすぎているもの(±0.05以下)は白黒に近いので除外
  • 彩度が低すぎ(0.3以下)の色もの特徴がなさすぎるので除外

で、その情報からNSMutableDictionaryを作る。

なんかすごい。NSDictionaryやNSArrayが@{}や@[]で書けるようになったのは知ってましたが、@(y)とかって書くとNSNumberに変換できるって知りませんでした。1年位前から出来たっぽい。

続いてはなんかすごい冗長な感じでもあるけどやってることはcolorBucketsっていう配列(NSMutableArray)に格納されている色情報と比較してYUVをピタゴラスの定理から2つの色の色差を計算して、それが近い(0.1未満)だった場合はほぼ同じ色と判断して、これまでチェックした色との平均をとって新しい色とします。全然違った色だった場合は新しく要素を配列に追加します。

colorBucketsの各要素はまた配列(NSMutableArray)になっていて、類似色だった場合に要素が追加されて、結果としてその数が類似色の数になって、類似色が一番多かった際にその平均の色が画像の特徴色として取り出される仕組みです。

colorBucketsの各要素の配列の先頭は類似色の平均(weightedAverageというメソッドで計算される)が入ります。bColorという変数の値として計算されています。一方で新しい要素を追加する際にも何やら取得した色とbColorの平均をとってnewColorを作っていますが、これ意味あるのかな。使ってない気がするけど…

類似色がなかった場合はcolorBucketsに新しい色として追加します。そして要素数でソートします。

そして最後にcolorBucketsをチェックしていますが何やらここにもマジックナンバーの香りが。色を出現数の多い順にチェックしますが、特定のUV領域の色が出現した場合は次の色を入れる的な処理が入っています。領域的にはUがマイナスでVが0から0.26の間。緑からオレンジ色的な領域にかけて?かと思いますが、これはUIに合わないから削った?のかまだ不明です。あとpopColとpopularColorとか変数名が微妙に気になります。

で、最後に取り出した色をUIColorに変換して(もとのRGBを利用)、メインスレッドにてコールバック関数を呼び出しています。

ということでここまで読んでわかったことは以下のとおりでした。

  • 画像を縮小して1ピクセルずつ調べることで特徴色を出している
  • RGBをYUVという色空間に変換することで、色の差を計算して類似色をまとめている
  • コードはかなり改善の余地がある(コピペコードとかマジックナンバーとか…)

ついでに調べていたんですけども、画像を特徴色を調べるを読んでいて、複数の色を出すならきちんとこちらのようなアルゴリズムを使ったほうがいいですし、上記アルゴリズムだと黄色の中に赤1点みたいなのは出ないなあと思いましたけど、1色だけ選ぶんならこれでいい…のかもですね。

HTTPヘッダにUNIX Timestampを入れるべきではないのか

APIを設計していてHTTPヘッダに情報を入れることがあるけれど、その際に時間を入れたい時があります。その際にどんな形式にすればいいのかなっていう話題です。

時間というのはいろいろな形式で表現が可能です。

形式名
RFC 1123 Thu, 17 Oct 2013 23:12:00 JST
ISO 8601 20131017T231200+0900
UNIX Timestamp 1381999405

で、この中で一番何がプログラム的に取り扱いやすいかといえばUNIX Timestamp、いわゆるエポック秒かなと自分は思っています。数値だし、それにタイムゾーンの概念がないので世界に単一のタイムゾーンしか無いと思っているエンジニアが変な値を入れる危険性も少ないと思われるから。しかも開発で使う言語はほぼすべてUNIX Timestampと自前の日時表現の変換の仕組みを提供しているし。他の形式だとフォーマットをこちらで指定しなくちゃいけなかったりして面倒な場合もあります。

で、APIには一人のユーザーからのアクセス数(あるいは1アプリケーションなど)を制限する「レートリミット」という仕組みを導入しているものが多くてその際にはX-RateLimit-Limit(あるいはX-Rate-Limit-Limit)をはじめとするヘッダを使うのがデファクトスタンダードっぽいです。

ヘッダ名 説明
X-Rate-Limit-Limit 単位時間あたりのアクセス上限
X-Rate-Limit-Remaining アクセスできる残り回数
X-Rate-Limit-Reset アクセス数がリセットされるタイミング

TwitterがAPI version 1.1で導入したのがはじめかな(調査中)と思うのですが、今は普通に使われています。例えばGitHubもちょっと名前は違いますがほぼ全く同じです。Foursquareもほぼ同じ。その他たくさんあります。こんなまとめもありました。

Rate-LimitとするかRateLimitとするかはどっちにするべきかよくわかりませんが、自分ならRate-Limitとするかなと思います。

そしてこの中にX-Rate-Limit-Reset(X-RateLimit-Reset)というヘッダがあって(Foursquareなどこれを返さないものもある)、これはAPIの利用制限が解除される時間を表しますが、UNIX Timestampで値が入ってるケースが多いのです。TwitterもGitHubもそうです。

でもこれに異議を唱えてる人がいて、Best Practices for Designing a Pragmatic RESTful APIという記事があり、その中でUNIX Timestampは使うな、解除までの秒数(デルタ秒)を使え!と強く主張していました。この記事を書いたVinay Sahniさんの主張では、HTTPでは使っていい日付形式はRFC1123で、UNIX Timestampは使えないんだから仕様に反してるんだよ!使うんだったらRFC1123形式を使うべきだ、と書かれています。

え、そうなのか、と改めてHTTP 1.1の仕様に立ち返ってみたところ、確かにHTTPでは時間を表すのに、以下の3つの形式か、デルタ秒を使わないといけないと書いてありました。

形式名
RFC 822, updated by RFC 1123 Sun, 06 Nov 1994 08:49:37 GMT
RFC 850, obsoleted by RFC 1036 Sunday, 06-Nov-94 08:49:37 GMT
ANSI C’s asctime() format Sun Nov 6 08:49:37 1994

定義で言うとrfc2616で以下のようになってます。

そしてたしかに例えばきちんと定義されているHTTPヘッダを見てみると、例えばRetry-Afterは以下のようになっています。

そうだったのか…ということでHTTPのヘッダにUNIX Timestampを使うのは厳密にはダメらしいんですけど、やっぱりUNIX Timestampはハンディだし使いたいなあと思っているこの頃です。

ちなみにHerokuも使っていますね。みんなこの辺を知っててあえてUNIX Timestampを選んだんでしょうか。

X-Request-Start: unix timestamp (milliseconds) when the request was received by the router

個人的にはUNIX Timestampを使い続けたいところ。例えばスマフォアプリのサーバクライアント連携とか、半ばプライベートなAPIの場合は使ってしまっていい気もしますが、一般公開するAPIの場合はどうすべきか悩むところです。デルタ秒だといつを基準としたデルタ秒だよ、という気もするし…まあDateヘッダで示された時間からですね。ただ、UNIX Timestampも随分利用されてきているので、仕様があとから追いついていく可能性はあるような気がします。

PNG画像のファイルサイズをなるべく小さくしたい

PNGやJPEGなんかのファイルはいろいろなメタ情報が含まれているのでそれを削除したり、カラーパレットや圧縮の最適化でサイズを小さくでき、そのための色々なツールが存在しています。ウェブサイトで公開するにも、モバイルアプリ内で使うにも、同じ画像ならファイルサイズが小さいに越したことはないので、そのへん色々調べました。今回はPNGについてです。

ウェブサービスだとKraken.ioとかTinyPNGとかがあります。ただこれらはウェブ上で使うものなので手作業が発生してちょっと面倒。せっかくならビルドプロセスに組み込んだりできたほうがいいので、ローカルで使えるツールのほうがいいです。Kraken.ioはAPI用意してくれていますが、月額$10で1000画像までとちょっとお高いです。

でMacでPNGの最適化を行えるツールをいろいろ試して何をするのが一番いいんだということを調査したのでこれはその結果とメモです。調査したツールは5種類。

以下のファイルを圧縮しました。これは自分のプロフィールアイコンとかに使ってる奴とほぼ同じもので、SPSTUDIOっていうサイトで作成したものです。

zopflipng fs8 re PNG画像のファイルサイズをなるべく小さくしたい

結果は以下のとおり

ツール名 コマンド サイズ 比率
PNGOUT pngout src.png -c2 -f3 dst.png 33,684 0%
AdvanceCOMP advpng -z -4 src.png 25,731 76.4%
OptiPNG optipng -o7 -strip all src.png 27,835 82.6%
Pngcrush pngcrush -brute src.png dst.png 27,528 81.7%
zopflipng zopflipng src.png dst.png 25,707 76.3%

 

最後のzopflipngはGoogleが2013年3月に公開したzopfliという新しいDeflate互換の圧縮アルゴリズムを使うもので、zopfliと一緒のリポジトリに入っているが、1.0.0のパッケージには入ってないのでリポジトリから直接持ってこないといけません。

結果を見ると、AdvanceCOMPzopflipngが圧倒的に圧縮率が高いです。この画像だとzopflipngに軍配が上がってますけど、他に試したものではAdvanceCOMPが勝ったりもする。おお、と思って調べたら、AdvanceCOMPは今年の3月にzopfliを使えるようにアップデートがなされていた(2005年以来のアップデート)のでした。それ以前にインストールしている人はすぐにアップデートした方がいいです。

今回の画像は24bitのPNGファイルですが、インデックスカラーにしたほうがサイズは減少するはずなので、pngquantというツールでインデックスカラー化しました。不可逆な圧縮ですが、今回の場合は見た目には変化はほぼわかりません。

ちなみにpngquantは最近2.0が公開され、アルゴリズムが見直されてクオリティが上がりました。サイト上にも「Ver.1はもう使わないように」って書いてあります。で、両方で圧縮してみたところ、やや差がでました。もしまだ1.xを使っているようならアップグレードしたほうが良さげです。

バージョン サイズ 比率
1.8.3 14,840 44.1%
2.0.1 14,450 42.9%

 

その後、pngrewriteというファイルで更にパレットを最適化しました。このツールの更新は2010年で止まっているのですが、とりあえず使ってみたところ、パレット数が256から253に減って25バイトだけ減りました。

状態 サイズ 比率
pngrewrite前 14,450 42.9%
pngrewrite前 14,425 42.9%

 

まあpngquantでカラーパレット最適化してるからあたり前なんだろうけど、じゃあなんで3個減らせたのか?とかまだ調べる余地はいろいろありそう。とりあえず先に進みます。

ツール名 コマンド サイズ 比率
PNGOUT pngout src.png -c2 -f3 dst.png 14,425 0%
AdvanceCOMP advpng -z -4 src.png 13,169 91.2%
OptiPNG optipng -o7 -strip all src.png 14,396 99.8%
Pngcrush pngcrush -brute src.png dst.png 14,394 99.8%
zopflipng zopflipng src.png dst.png 13,156 91.2%

 

今度も若干zopflipngに軍配があがったです。ちなみにzopflipngが出力した結果はこちら。

zopflipng fs8 re PNG画像のファイルサイズをなるべく小さくしたい

画像によっておそらくツールごとの差異は違ってくると思うのですが、とりあえずAdvanceCOMPとzopflipngのどちらかを使うのが良さげですね。

ちなみにGUIでやるなら今回調べたツールはすべてImageOptimに統合されているみたいです。こちらためしてみると、24bitの場合はいまいちだったのですが、インデックスカラーの場合はzopflipngに迫るサイズになりました。

状態 サイズ 比率
24bit 26,062 42.9%
インデックスカラー 13,157 42.9%

 

あと最初に言及した、ウェブサービスとして公開されているツールを2つ調べました。

ツール名 サイズ 比率
オリジナルファイル 33,684 0%
Kraken.io LossLess 25,734 76.4%
Kraken.io Lossy 13,221 39.2%
TinyPNG 14,571 43.3%

 

TinyPNGはインデックスカラー化しかできませんが、KrakenはLossLessとLossyが選べるので両方を比較しました。けど今回の場合はどちらもコマンドラインツールのコンボにかなわなかった感じなので、AdvanceCOMPかzopflipng手元で処理してしまってもよい感じです。