tech.kayac.com

1. あえてECMAScript3.0以前の実行環境を使う

あえてECMAScript3.0の実行環境を使うようにしましょう。そしてATNDで好みの男がいたらLT参加を告知し、わざとらしく発表準備段階でコンソールを出していじってみましょう。そして「あ~ん! この実行環境本当にマジでチョームカつくんですけどぉぉお~!」と言って、男に「どうしたの?」と言わせましょう。言わせたらもう大成功。「ECMAScriptとか詳しくなくてぇ~! ずっとコレ使ってるんですけどぉ~! Object.keysが使えないんですぅ~! ぷんぷくり~ん(怒)」と言いましょう。だいたいの男は新しい実行環境を持ちたがる習性があるので、ECMAScript5の実行環境を使っているはずです。

そこで男が「新しい実行環境にしないの?」と言ってくるはず(Object.prototypeの拡張を勧める男はその時点でガン無視OK)。そう言われたらあなたは「なんかなんかぁ~! 最近ECMAScript4が人気なんでしょー!? あれってどうなんですかぁ? 新しいの欲しいんですけどわかんなぁぁああい!! 私かわいそーなコ★」と返します。すると男は「ECMAScript5でしょ? ECMAScript4は破棄されたよ。本当に良くわからないみたいだね。どんな環境で動かしてるの?」という話になって、次のShibuya.js開催日にふたりでust観戦ができるというわけです。あなたの女子力が高ければ、男がLTしてくれるかも!?

2. jQueryのSelectorで@を使うとモテる

jQueryで要素を取得するためのSelectorに 「@」を使うと、jQuery派の男性ユーザーは「なんかこのコード.querySelectorAll使ってなさそうだなぁ」や「@を削除するだけで早くなるかも」と思ってくれます。コード上では実際の呼び出し回数よりも記述回数の方が印象強く相手に伝わるので 「@」 を多用することによって、男性はそのコードを簡単な修正で高速化が可能と勘違いしてくれるのです。もちろん処理速度が問題になる箇所では直接DOM APIを駆使して高速に動作するコードを記述しましょう。

3. とりあえず男には「えー!なにそれ!? コード見たーい♪」と言っておく

勉強会の2次会などで男が女性に話すことといえば動的補完や単体テストの話ばかり。よって、女性にとってどうでもいい話ばかりです。でもそこで適当に「へぇーそうなんですかぁ~?」とか「よくわかんないですけどすごいんですねぇ」と返してしまうと、さすがの男も「この女MDCも知らないな」と気がついてしまいます。ダメ女だとバレたら終わりです。そこは無意味にテンションをあげて、「えー! なにそれ!?コード見たーい♪」と言っておくのが正解。たとえ興味がないコードでも、ブレークポイントdebugger statementでその場を乗り切りましょう。積極的にコードを読んでくれる女性に男は弱いのです。

いろいろとコードを読んだあと、「〇〇は〇〇で、〇〇が〇〇なんですね! 覚えたぞぉ! メモメモ!」とコメントすればパーフェクト。続けて頭に指をさしてくるくる回しつつ「CREATE TABLE! INSERT INTO!」と言って、「どうしたの?」と男に言わせるのもアリ。そこで「私のWeb SQL Databaseに記録しているのでありますっ☆」と言えばJS力アップ! そこでまた男は「この子Chrome使いかも!?」と思ってくれます。Web SQL DatabaseはW3Cでの仕様策定が断念されましたがたまにはSQLも使いたいですからね。

4. TLではWeb Workersが使えない女をアピールせよ

Twitterにログインしたら、真っ先にWeb Workersを使ったコードをjsdo.itへアップして「あーん! 私これ実行できないんですよねぇ~(悲) #jsdoit 」とつぶやきましょう。するとほぼ100パーセント「どうして? HTML5関係のAPIをサポートした環境がないの?」と聞かれるので、「環境はあるし実行したいけど実行できないんですっ><」と返答しましょう。ここでまた100パーセント「環境はあるのにどうして実行できない?」と聞かれるので、3~5ツイートほど間をおいてからボソッとこうつぶやきます。「……だって、……だって、WorkerスレッドからはpostMessageでしか通信できないじゃうじゃないですかぁっ! Workerさんかわいそうですぅ! まだ生まれてまもないのにぃぃ~(悲)。DOMすら触れないんですよ…… #jsdoit 」とアイコンを震わせてつぶやくのです。

その瞬間、あなたのJS力がアップします。きっと男は「なんて優しい天使のようなコなんだろう! 絶対にゲットしてやるぞ! コイツは俺の女だ!」と心のなかで誓い、あなたに惚れ込むはずです。意中の男と付き合うことになったら、そんなことは忘れて好きなだけWeb Workersを使って大丈夫です。「使えないんじゃなかったっけ?」と言われたら「大丈夫になった」とか「慣れた」、「SharedWorkerなら複数インスタンスから参照できる」と言っておけばOKです。

乗り換え以外で初めてアメリカ行きました。ago@kyo_ago)です。

すでに先週のこととなってしまいましたが、jsdo.it上でSVG Girlというコンテンツを発表したのでご紹介させていただきます。
(現在IE9のリリースに合わせて英語版のみ公開しています。日本語版は26日の日本語版IE9リリースにあわせて公開を予定しています)

SVG Girlって?

SVG GirlはSVGを使ったアニメーション作品で、動画コンテンツの中身をすべてSVGのみで表現しています。

先週開催されたMicrosoft主催のMIX11のキーノートでも発表され(00:08:40ぐらいからご覧いただけます)、大画面に表示されたアニメーションで会場をわかせました。

SVGって?

SVGはテキストで表現されたベクターデータで、XMLで線画情報を保持しています。

ブラウザ上からは通常のDOM API経由で操作することが可能で、JS、CSSから内容を変更することが可能な画像形式です。

内部実装

現在の実装では.ai形式で作成されたアニメーションの各コマを.svg形式で出力したあと、JSでDOM Treeにすべてのコマを追加してsetIntervalでdisplay:noneしています。

当初アニメーション全体をそのままSVGへ変換したところ、未圧縮状態で150MB以上になり、読み込みに3分、読み込み後のメモリ使用量は1.2GBを超えていました。

その状態から最適化を行い、現在では未圧縮状態で40MB、読み込みは早い機種で数秒、メモリ使用量も400MB程度まで軽量化されています。

高速化

高速化に関してはCDNの利用、svgのレイヤー化(今回はsvg内でのレイヤー分けではなく、html上のz-indexを使ってレイヤー化しています)が主な点ですが、それ以外にもJS内で以下のような実装を行っています。

.innerHTML以外の使用

svgはサーバ上からtextとして読み込まれますが、その後DOMノードへ変換するためtextから要素へ変換するための処理が必要となります。

これに関して当初は.innerHTMLを使用していましたが、最終的には以下のような実装を行うことでinnerHTMLに対して2割程度高速化できました。

var range = document.createRange();
if (range.createContextualFragment) {
    parent.appendChild(range.createContextualFragment(text));
    return parent.lastChild;
};
div.insertAdjacentHTML
    ? div.insertAdjacentHTML('afterBegin', text)
    : div.innerHTML = text
;
return parent.appendChild(div.firstChild);

具体的にはcreateContextualFragmentか、insertAdjacentHTMLが使える場合にはinnerHTMLではなくそちらを使用しています。

style要素の動的書き出し

SVG Girlではユーザが簡単に画面上の色を変えることができ、そこで変更された色で動画を再生する機能を持っています。

当初この機能は変更された色情報を元にsvgを読み込み直し、svg text内の色情報を正規表現で置き換える実装になっていましたが、この方法では再読み込み時に時間がかかってしまうため以下のような方法で実装しました。

var selector = '';
var method = $.browser.msie ? 'toLowerCase' : 'toUpperCase';
for (var i = 0, c1; c1 = color[i]; i += 2) {
    var c2 = color[i + 1][method]();
    c1 = c1[method]();
    selector += '[fill="'+c1+'"]{fill:'+c2+'}\n';
};
$('<style class="svg_color_edit" id="svg_color_edit_base">'+selector+'</style>').appendTo('head');

具体的にはsvg要素自体に変更を加えるのではなく、style要素で色情報を書き出すことで、色変更実装の簡略化、再読込無しでの色変更の実施ができるようになっています。

上記コードではブラウザごとに処理を切り分けていますが、これはIE9で属性指定が小文字でしか一致しなかったためです。
(今回唯一ブラウザ実装の差異が発生した点でした)

カヤックではアニメーションが好きな技術者も募集しています!

※この記事はnginxの現時点での最新stable 0.8.54を使っています。

2回目の投稿になります、sugyanです こんにちは。
最近、jsdo.itでちょっとしたAPIを作ってみているのですが、連続で大量のリクエストが来るのはちょっと困るので、防御策としてnginxのリクエスト制御モジュール"HttpLimitReqModule"を導入してみることにしました。
http://wiki.nginx.org/HttpLimitReqModule

何も設定しない場合

まずは普通のnginx設定でhttpサーバを立ち上げて、動かしてみます。

worker_processes  1;

error_log  logs/error.log  info;

events {
    worker_connections  256;
}

http {
    log_format  test '$remote_addr - [$time_local] "$request" $status "$http_user_agent" $request_time';
    access_log  logs/access.log  test;

    server {
        listen       80;
        server_name  localhost;
    }
}

パフォーマンス測定にはhttp_loadコマンドを使ってみます。
http://acme.com/software/http_load/

$ cat urls
http://localhost/
$ http_load -parallel 10 -fetches 1000 urls
1000 fetches, 10 max parallel, 151000 bytes, in 0.258776 seconds
151 mean bytes/connection
3864.35 fetches/sec, 583516 bytes/sec
msecs/connect: 0.132611 mean, 0.44 max, 0.05 min
msecs/first-response: 2.44852 mean, 31.4 max, 0.285 min
HTTP response codes:
  code 200 -- 1000

あっという間に1000リクエストくらい捌いてしまいますね。

limit_reqを設定する

設定ファイルのhttpディレクティブに、HttpLimitReqModuleの設定を追加してみます。

http {
    log_format  test '$remote_addr - [$time_local] "$request" $status "$http_user_agent" $request_time';
    access_log  logs/access.log  test;

    limit_req_zone       $binary_remote_addr  zone=one:10m  rate=1r/s;
    limit_req_log_level  info;

    server {
        listen       80;
        server_name  localhost;
        
        location = / {
            limit_req  zone=one;
        }                
    }
}
$ http_load -parallel 10 -fetches 1000 urls 2> /dev/null
1000 fetches, 10 max parallel, 212938 bytes, in 0.068365 seconds
212.938 mean bytes/connection
14627.4 fetches/sec, 3.11472e+06 bytes/sec
msecs/connect: 0.215024 mean, 0.574 max, 0.05 min
msecs/first-response: 0.448167 mean, 1.291 max, 0.173 min
999 bad byte counts
HTTP response codes:
  code 200 -- 1
  code 503 -- 999

処理自体はすぐに終わりますが、1回だけ200が返り 残りのリクエストはすべて503になっています。error.logは以下のように出力されました。

2011/03/25 17:03:35 [info] 6607#0: *2 limiting requests, excess: 1.000 by zone "one", client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.0", host: "localhost"
2011/03/25 17:03:35 [info] 6607#0: *3 limiting requests, excess: 1.000 by zone "one", client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.0", host: "localhost"
2011/03/25 17:03:35 [info] 6607#0: *4 limiting requests, excess: 1.000 by zone "one", client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.0", host: "localhost"
...

"zone=one"で指定した通りに制限されているのが確認できますね。試しに3秒間リクエストを送り続けてみます。

$ http_load -parallel 10 -seconds 3 urls 2> /dev/null
16254 fetches, 10 max parallel, 3.4617e+06 bytes, in 3.00011 seconds
212.975 mean bytes/connection
5417.8 fetches/sec, 1.15386e+06 bytes/sec
msecs/connect: 0.783652 mean, 980.029 max, 0.043 min
msecs/first-response: 0.548379 mean, 123.302 max, 0.059 min
16251 bad byte counts
HTTP response codes:
  code 200 -- 3
  code 503 -- 16250

"rate=1r/s"(秒間1リクエスト)に対し3秒間なので3回だけ200が返り、あとはやはりすべて503です。

limit_reqのburstを設定する

"limit_req"の設定では"burst"という値を指定できます。制限を超える数のリクエストが来た際にその値の数だけ遅延してレスポンスを返すようにしてくれるようです。デフォルト値は0です。

            limit_req  zone=one  burst=5;
$ http_load -parallel 10 -fetches 1000 urls 2> /dev/null
1000 fetches, 10 max parallel, 212628 bytes, in 5.00064 seconds
212.628 mean bytes/connection
199.974 fetches/sec, 42520.2 bytes/sec
msecs/connect: 0.138747 mean, 0.357 max, 0.064 min
msecs/first-response: 15.1978 mean, 5000.32 max, 0.072 min
994 bad byte counts
HTTP response codes:
  code 200 -- 6
  code 503 -- 994

200が返ってくる回数が増えました。error.logは以下のようになっています。

2011/03/25 17:37:49 [info] 7316#0: *2 delaying request, excess: 1.000, by zone "one", client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.0", host: "localhost"
2011/03/25 17:37:49 [info] 7316#0: *3 delaying request, excess: 2.000, by zone "one", client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.0", host: "localhost"
2011/03/25 17:37:49 [info] 7316#0: *4 delaying request, excess: 3.000, by zone "one", client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.0", host: "localhost"
2011/03/25 17:37:49 [info] 7316#0: *5 delaying request, excess: 3.999, by zone "one", client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.0", host: "localhost"
2011/03/25 17:37:49 [info] 7316#0: *6 delaying request, excess: 4.999, by zone "one", client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.0", host: "localhost"
2011/03/25 17:37:49 [info] 7316#0: *7 limiting requests, excess: 5.999 by zone "one", client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.0", host: "localhost"
2011/03/25 17:37:49 [info] 7316#0: *8 limiting requests, excess: 5.999 by zone "one", client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.0", host: "localhost"
...

制限を超えていても最初の5リクエストはいきなり503を返さず、レスポンスを遅延させているようです。access.logを見ると遅延して返ってきているのが確認できます。

...
127.0.0.1 - [25/Mar/2011:17:37:49 +0900] "GET / HTTP/1.0" 503 "http_load 12mar2006" 0.000
127.0.0.1 - [25/Mar/2011:17:37:49 +0900] "GET / HTTP/1.0" 503 "http_load 12mar2006" 0.000
127.0.0.1 - [25/Mar/2011:17:37:49 +0900] "GET / HTTP/1.0" 503 "http_load 12mar2006" 0.000
127.0.0.1 - [25/Mar/2011:17:37:50 +0900] "GET / HTTP/1.0" 200 "http_load 12mar2006" 1.000
127.0.0.1 - [25/Mar/2011:17:37:51 +0900] "GET / HTTP/1.0" 200 "http_load 12mar2006" 2.001
127.0.0.1 - [25/Mar/2011:17:37:52 +0900] "GET / HTTP/1.0" 200 "http_load 12mar2006" 3.000
127.0.0.1 - [25/Mar/2011:17:37:53 +0900] "GET / HTTP/1.0" 200 "http_load 12mar2006" 3.999
127.0.0.1 - [25/Mar/2011:17:37:54 +0900] "GET / HTTP/1.0" 200 "http_load 12mar2006" 4.999

nodelay

burst値指定の後に"nodelay"をつけるとこの遅延がなくなり、すぐにレスポンスが返ってくるようになります。

            limit_req  zone=one  burst=5  nodelay;
$ http_load -parallel 10 -fetches 1000 urls 2> /dev/null
1000 fetches, 10 max parallel, 212628 bytes, in 0.066109 seconds
212.628 mean bytes/connection
15126.5 fetches/sec, 3.21632e+06 bytes/sec
msecs/connect: 0.226904 mean, 0.496 max, 0.052 min
msecs/first-response: 0.410101 mean, 1.007 max, 0.142 min
994 bad byte counts
HTTP response codes:
  code 200 -- 6
  code 503 -- 994

結果は先ほどと同じですが、レスポンスは一瞬で返ってきています。わざわざ遅延させたくない、5回くらいの制限オーバーは大目に見る、というときに使えば良いのでしょうか。

まとめ

HttpLimitReqModuleを使うことで受け付けるリクエスト量を制限できることが確認できました。APIを提供するサーバのフロントなどで使用すると有用だと思います。
デフォルトでこんな便利機能が付いているnginx、優秀ですね。


カヤックではnginxを使いこなせる技術者も募集しています!