自宅環境

Mac Book Pro を買ってしばらく経つが、自宅で使うときの環境を少し改善しようとディスプレイを買った。あと、クラムシェルモードで使えるようにマウスとキーボードも。


薄くいい感じの大きさだったのと値段も手頃だった。 ただ、電源ボタンなどの反応が良くない。

Apple Magic Mouse - Apple Store(日本), Apple Wireless Keyboard (US) - Apple Store(日本)
Apple 製品で揃えてみたけれど、やっぱり HHK を使いたい。

グリーンハウス Deep Color/3D映像 フルHD映像対応 HDMI切換器 3台用 リモコン付 Input3+Output1ポート GH-HSW301

グリーンハウス Deep Color/3D映像 フルHD映像対応 HDMI切換器 3台用 リモコン付 Input3+Output1ポート GH-HSW301


他の機器と切り替えられるように購入。IN を切り替えるにはボタンやリモコンで切り替えられるようになっているけれども、新しく接続したらその画面に切り替わってしまうので何か作業中に新しく接続する場合は要注意かな。

mod_mruby を使った Web アプリ

これは mod_mruby ngx_mruby Advent Calendar 2014 の 15 日目(12/15) の投稿です。

mod_mruby を使った Web アプリ

きっかけは、ISUCON で mod_mruby を使ってみたいなーと思い、そのために高速できるポイントやボトルネックの探り方を見つけるための演習として考えたものです。 ただ、実際には ISUCON 予選には間に合わず、今回の Advent Calendar のために作成したものになります。

どういうアプリを作ったのか?

何らかのストレージに保存し、大量のアクセスを捌く Web アプリを想定して、今回は短縮 URL アプリを作成しました。長い URL から短い URL を生成し、長い URL を書かずに済むというものです。 また、これなら機能拡張をして検証するのが簡単かなと。例えば、ユーザーごとに短縮 URL を管理できるようにしたり、そのためにユーザーの認証を行ったり、アクセス制御をつけたり、はたまたリクエスト処理の効率を考慮した短縮URLハッシュの生成など、その後機能を拡張させて検証できると思います。

準備

今回使用した mrbgems は mruby-redis と mruby-json です。 mruby-redis には、今回必要だったハッシュ系の API がなかったので、少し手を入れました。

設計

入力フォームから URL を入力して、短縮 URL を生成する処理と短縮 URL にアクセスした時にオリジナルの URL にリダイレクトする処理を実装します。

Apache の設定

# オリジナルの URL から、短縮 URL を生成する
<Location /api/v1/shorten>
    mrubyHandlerMiddle /home/vagrant/opt/apache/mruby/shorten.rb
</Location>

# 短縮 URL から、オリジナルの URL にリダイレクトさせる
<Location /go>
    mrubyHandlerMiddle /home/vagrant/opt/apache/mruby/go_original_url.rb
</Location>

入力フォーム

ここで入力した URL を jQuery/api/v1/shorten にリクエストし、生成した URL を表示します。Web API とは JSON 形式でやりとりをします。

shorten_url.png

<!DOCTYPE html>
<html>
    <head>
        <title>Shorten URL</title>

    </head>
    <body>
        <h1>Shorten URL</h1>
        <input type="text" id="url" />
        <button id="shorten-url">shrten</button>
        =
        <span id="result"></span>
    </body>

<script type="text/javascript" src="https://code.jquery.com/jquery-1.11.1.js"></script>
<script type="text/javascript">
<!--
$(function () {
    $("#shorten-url").on("click", function () {
        var url = $("#url").val();
        if (url === "") {
            alert("Please enter the URL");
            return;
        }
        $.ajax({
            type: "POST",
            contentType: 'application/json',
            url: "/api/v1/shorten",
            data: JSON.stringify({ url: url }),
        }).done(function(res) {
            $("#result").text(res.short_url);
        });
    });
})
//-->
</script>
</html>

短縮 URL を生成する

入力フォームから送られてきた JSON リクエストを解析し、短縮 URL を生成し、その URL を返します。

短縮 URL の生成は、シーケンシャルな番号を生成し、それをそのまま使うようにしています。 また、短縮 URL のリクエストを受け付けるサーバーの URL が固定で指定しています。

error_res = JSON::stringify({})

req = Apache::Request.new
req.content_type = "application/json"

unless req.method == "POST"
  Apache.rputs(error_res)
  Apache::return(Apache::OK)
end

post_data = JSON::parse(req.body)

url = post_data["url"]

# Redis config
host     = "127.0.0.1"
port     = 6379
database = 8

redis = Redis.new host, port
redis.select database

request_num = "request_number"

unless redis.exists?(request_num)
  redis.set request_num, "0"
end

# generate a key
next_num = redis.incr(request_num)
url_key = "#{next_num}"

redis.hset "shorten_urls", url_key, url

Apache.rputs JSON::stringify({"url" => url, "short_url" => "http://localhost/go/#{url_key}"})

短縮 URL から、オリジナル URL にリダイレクトさせる

リクエストの URL からハッシュ値を取り出し、Redis からオリジナルの URL を取得し、リダイレクトします。

error_res = JSON::stringify({})

req = Apache::Request.new

unless req.path_info.index('/') == 0
  req.content_type = "application/json"
  Apache.rputs(error_res)
  Apache::return(Apache::OK)
end

url_key = req.path_info[1..-1]

# Redis config
host     = "127.0.0.1"
port     = 6379
database = 8

redis = Redis.new host, port
redis.select database

unless redis.hexists?("shorten_urls", url_key)
  req.content_type = "application/json"
  Apache.rputs(error_res)
  Apache::return(Apache::OK)
end

original_url = redis.hget "shorten_urls", url_key

Apache.rputs(original_url)

req.headers_out["Location"] = original_url
Apache::return(Apache::HTTP_MOVED_PERMANENTLY)

まとめ

予定では負荷を与えてどれくらいリクエストを処理できるかを調べてみたり、どういう部分に注意したらいいのか、MySQL との比較を載せる、予定でした。あと、ngx_mruby との比較も。それらの今回できなかったことは、今後やっていくこととします。(すみません・・・実装も幾つか手抜きになってしまっていて)

今回 mruby で Web アプリを作ってみましたが、Web API 側を mruby で実装するのは、簡単にできて良さそうだと思いました。mod_mruby と ngx_mruby で共通の作りにしておけば、アプリの特性や環境によって使い分けることができたりすると思います。

mod_mruby で Web アプリを作ることはあまりないかもしれませんが、HTML 生成などがないような Web API なら使いどころはあるのかなと。そして、mrbgems が充実することでさらに可能性が広がっていくような気がします。

template engine があったら、DB から取得した値を表示するのに楽になったりするのかなと思ったり。

明日は、hkusuさんの「mod_mrubyで静的ファイルをリバースプロキシする」です!

ISUCON 4 予選に参加した - チーム「ヤキトリ缶(タレ)」

昨年に引き続き、今年も参加してみた。 メンバーも昨年と同じく、@hisayosh@sechiro。昨年のチーム名は「勝浦タンタンメン」でした。

予選終了時のスコアは 30000 弱。最終スコアは benchmarker v2 で計測したものになるということですが、予選突破は難しい気がします。

やったこと

言語は Python

まずは、調査用のインスタンスを立ち上げ、アプリの仕様とベンチマークの動作を調べていました。
nginx にログを仕掛け、どんなリクエストを送っているか、そのリクエストの処理時間がどれくらいかかっているのかを見て、ベンチマークが単調な動きをしていること、そして、アプリが単純な認証しか機能がなかったこと。

log_format  perflog  '[$time_local] "$request" $request_length '
                     '$status $body_bytes_sent $request_time';
access_log /var/log/nginx/access.log perflog;

それからは、Redis を使うためにサーバーとクライアントライブラリをインストールし、準備を始める。おおまかに ip と users キャッシュすることを目標として、まずは ip から始めた。

ip 判定は Redis キャッシュから判定するように実装し、初期データを登録する python スクリプトを作成する。
users 側のデータ設計を考え、これも初期データを登録する python スクリプトの準備に取りかかる。

users の実装途中だったが、ip 判定用のデータができたところで、初期データの準備ができので、そちらを継続することに。
しかし、実際に試してみたところ Fail するように。ログインの判定がうまくいかなくなってしまう。

調べたが、原因をつかめず、時間切れとなった。

振り返ってみて

チーム全体では前回と比べ、やろうとしていたことに対して、できたことが増えたと思う。戦略の立て方は良かったと思う。
ただ、自分のタスクがうまくいかなかったのはかなりの痛手だった。 実装した部分ではまるとその解決に時間がかかる。事前準備でもっと Python に時間を使えていたらと思う。案があるのに実装できなかったのが悔しい。
もっと他に方法はないかと考える余裕くらいは持てたら良かったと。

しかしながら、楽しかった!準備をしている時間を含めてひさしぶりに楽しい時間を過ごした気がした。 やはり、こういう環境に身を置けるのが一番いいのだと、改めて思った。

参加チームが 200 弱という規模になり、運営やそれにかかる判断が難しいとは思いますが、運営スタッフのみなさまおつかれさまでした!!