「過負荷に耐えるWebの作り方 〜国民的アイドルグループ選抜総選挙の舞台裏」を読んだ

読んでみた

秒間10,000アクセスの処理が要件の、AKB48選抜総選挙システムを構築した事例の解説本です。普段そんなにアクセスが来るシステムを触っていないということで、どんな仕組みなのかが気になって読んでみました。

過負荷に耐えるWebの作り方 ~国民的アイドルグループ選抜総選挙の舞台裏 (Software Design plus)

過負荷に耐えるWebの作り方 ~国民的アイドルグループ選抜総選挙の舞台裏 (Software Design plus)

システム面でおもしろかったこと

基本構成は真っ当な感じで、

  • LVSでロードバランサ構築
  • リバースプロキシはVanish
  • アプリケーションはJavaで開発
  • サーブレットコンテナはJetty

なのですが、

『DBにはインメモリデータベースのVoltDBを採用』

というのが珍しいポイントでした。

実はこの本を読むまでVoltDBを知らなかったのですが、

  • インメモリデータベースなのでIO負荷が低く高速
  • ACID特性がある
  • SQLが使える
  • 台数を増やすことでリニアにスケールする

ということで、NoSQLとRDBのいいとこ取り、といった特長を持っているようです。

ただしノードにまたがる集計、更新処理ではノード間の通信量が多くなり、一定量を超えると処理ができないケースがあったとのことで、現状ではあまりメジャーでないこともあり、注意が必要かもしれません。

なお、本の事例では障害時のデータ復旧のためにアプリケーション側でローカルファイルにデータを書き込んでいるとのことでしたが、VoltDBにはデータ永続化機能としてスナップショットを保存する機能*1があるようなので、用途によってはこちらでもいけそうです。

その他の感想

「秒間10,000アクセスが処理できること」という要件ですが、想定では瞬間最大値として秒間10,000アクセスあるかもしれないという話だったのが、いつの間にか営業担当者とお客さんの間で連続秒間10,000アクセスという話になっていた(しかも実際は全然そんなにアクセスが集中しなかった)ということで、無駄じゃんそれ…というのが正直な感想でした。

とは言え、こういった極端な要件をこなすにあたっては実現の上で多くの知見が得られる面もあるので、一度ぐらいはあってもいいのかもしれませんね。

SinatraでModularスタイルのときのテストでハマってた話

ざっくりまとめると

SinatraModulerスタイルで書いたときは、RSpecなどでテストするときに上書きするRack::Testのappメソッドを

def app
  Sinatra::Application
end

ではなく

def app
  HelloSinatra
end

みたいに、アプリケーションのクラス(上記の場合はHelloSinatra)を返すようにしないとテストがうまく動かないという話です。

環境

なにごと?

Testing Sinatra with Rack::Testを見つつこんな感じのコードとテストを書いてたわけですよ。

# app.rb
require 'sinatra/base'

class HelloSinatra < Sinatra::Base
  get '/' do
    'Hello, Sinatra!'
  end
end
# spec/requests/hello_spec.rb
ENV['RACK_ENV'] = 'test'

require File.join(File.dirname(__FILE__), '..', '..', 'app.rb')
require 'sinatra'
require 'rack/test'
require 'rspec'

describe 'HelloSinatra' do
  include Rack::Test::Methods

  def app
    Sinatra::Application
  end

  it 'should be OK' do
    get '/'
    expect(last_response).to be_ok
  end
end

で、テストすると失敗するわけです。

$ rspec
F

Failures:

  1) HelloSinatra should be OK
     Failure/Error: expect(last_response).to be_ok
       expected ok? to return true, got false
     # ./spec/requests/hello_spec.rb:18:in `block (2 levels) in <top (required)>'

Finished in 0.03914 seconds
1 example, 1 failure

Failed examples:

rspec ./spec/requests/hello_spec.rb:16 # HelloSinatra should be OK

なんでかなーと思ってModular vs. Classic Style - Sinatra: READMEを見つつ考えてみると、Testing Sinatra with Rack::TestSinatra+RSpecのサンプルはClassicスタイルで

require 'sinatra'
get '/' do
  'Hello world!'
end

って感じで書いてあります。 で、Classicスタイルだとconfig.ruは

require './app'
run Sinatra::Application

のようにSinatra::Applicationを起動します。 これはきっとClassicスタイルの場合はルータを定義したりするとSinatra::Applicationが拡張されていくってことかな(TODO: ちゃんと調べる)。

それに対してModulerスタイルで

require 'sinatra/base'
class HelloSinatra < Sinatra::Base
  get '/' do
    'Hello, Sinatra!'
  end
end

ってやるときは、config.ruは

require './app'
run HelloSinatra

のように、アプリケーションのクラスから起動します。

…ということはModulerスタイルの場合、RSpecでもappメソッドはSinatra::ApplicationではなくHelloSinatraを返さなきゃダメなんじゃないだろうか、ということで

# spec/requests/hello_spec.rb
ENV['RACK_ENV'] = 'test'

require File.join(File.dirname(__FILE__), '..', '..', 'app.rb')
require 'sinatra'
require 'rack/test'
require 'rspec'

describe 'HelloSinatra' do
  include Rack::Test::Methods

  def app
    HelloSinatra # ←Sinatra::Applicationから変更
  end

  it 'should be OK' do
    get '/'
    expect(last_response).to be_ok
  end
end

のように書きなおしてみると…

$ rspec
.

Finished in 0.03378 seconds
1 example, 0 failures

うまくいった!

まとめ

というわけで、Sinatraの動きをもう少しちゃんと調べましょうというお話でした。

ruby-opencvの進捗の話(2014年2月版)

はじめに

なんか気がついたら2014年も1ヶ月が過ぎてますが、ruby-opencvのお話です。 前回からバージョンもだいぶ上がり、機能も地味にいろいろ追加されてるので、ちょっとだけ紹介します。

APIリファレンス的なものを(ようやく)用意しました

前からちょっとずつYARDで書いてたアレをようやくマージしました。 rubydoc.infoで見れるのでどうぞ。

Documentation for ruby-opencv

微妙に網羅できてないのもありますが、よく使うやつはだいたい書いてあります。

FaceRecognizerを追加しました

FaceRecognizer #17 でご要望を頂いておきながらずーっと放っておいたFaceRecognizerが(v0.0.11から)使えるようになりました。 公式のチュートリアル Face Recognition with OpenCV に相当するサンプルも用意してあるので、興味のある方はぜひどうぞ。

https://github.com/ruby-opencv/ruby-opencv/tree/master/examples/facerec

FaceRecognizer(EigenFaces)のサンプルコード

せっかくなので、FaceRecognizerのEigenFacesを使って顔認識してみましょう。 Ubuntu 13.10 + Ruby 2.1.0-p0(x86_64-linux) + ruby-opencv 0.0.12 で動作確認していますが、だいたいどの環境でも同じように動くはずです。

1. 認識用の顔画像を AT&T Facedatabase から取ってくる

http://www.cl.cam.ac.uk/research/dtg/attarchive/facedatabase.html から認識用の顔画像を取ってきて展開します。

$ wget http://www.cl.cam.ac.uk/Research/DTG/attarchive/pub/data/att_faces.zip
$ unzip att_faces.zip

2. テストデータを用意する

学習用に顔画像をラベルと対応付けたファイルを作成します。 OpenCV公式チュートリアルこれを使ってもいいですが、 Ruby版のcreate_csv.rbも作ったので今回はこっちを使いましょう。

# create_csv.rb
if ARGV.size != 1
  puts "usage: ruby #{__FILE__} <base_path>"
  exit
end

BASE_PATH = ARGV[0]
SEPARATOR = ';'

label = 0
Dir.glob("#{BASE_PATH}/*").each { |dir|
  if FileTest::directory? dir
    Dir.glob("#{dir}/*") { |filename|
      puts "#{filename}#{SEPARATOR}#{label}"
    }
    label += 1
  end
}

画像ファイルのフォルダを引数で指定して実行します。 結果は標準出力に出るので、適当にリダイレクトしてください。

$ ruby create_csv.rb att_faces > at.txt

実行すると、下記のように 画像ファイル名;ラベル というフォーマットで対応付けのファイルが作成されます。

$ cat at.txt
att_faces/s34/2.pgm;0
att_faces/s34/3.pgm;0
att_faces/s34/8.pgm;0
att_faces/s34/4.pgm;0
att_faces/s34/5.pgm;0
att_faces/s34/10.pgm;0
att_faces/s34/9.pgm;0
att_faces/s34/7.pgm;0
att_faces/s34/6.pgm;0
att_faces/s34/1.pgm;0
...

3. 顔認識してみる

こんな感じのプログラム(eigenfaces.rb)を作ります。

# eigenfaces.rb
require 'opencv'
include OpenCV

def read_csv(filename, sepalator = ';')
  images = []
  labels = []
  open(filename, 'r') { |f|
    f.each { |line|
      path, label = line.chomp.split(sepalator)
      images << CvMat.load(path, CV_LOAD_IMAGE_GRAYSCALE)
      labels << label.to_i
    }
  }
  [images, labels]
end

if ARGV.size < 1
  puts "usage: ruby #{__FILE__} <csv.ext>"
  exit 1
end

fn_csv = ARGV.shift
output_folder = ARGV.shift

# 顔画像を読み込む
images, labels = read_csv(fn_csv);

height = images[0].rows;

# 識別対象の顔画像は学習対象とは別にしておく
test_sample = images.pop
test_label = labels.pop

# Eigenfacesのモデルを作成して学習
model = EigenFaces.new
model.train(images, labels)

# 識別処理
# 識別結果のラベルと一致度が返ってくる
predicted_label, predicted_confidence = model.predict(test_sample)

# 結果を表示
puts "Predicted class: #{predicted_label} / Actual class: #{test_label}"
puts "Confidence: #{predicted_confidence}"

w1 = GUI::Window.new('Predicted')
w2 = GUI::Window.new('Actual')

w1.show images[predicted_label]
w2.show images[test_label]

GUI::wait_key

で、実行します。引数は2で作ったファイルのファイル名です。

$ ruby eigenfaces.rb at.txt

実行結果はこんな感じ(左が識別結果で右が実際の顔)。どうやらそれっぽく動いているようです。

f:id:ser1zw:20140211002306p:plain

そんなわけで

ruby-opencvの進捗状況でした。

modern.IEで配布されているWindowsの仮想マシンを64bit版Ubuntuにインストールしようとした話

環境

なにごと?

マイクロソフトがIEでのWebページの検証のために、modern.IE というサイトで各種バージョンのIEが入った仮想マシンを配布しています。

Linuxでの検証用にはVirtualBoxのVMが配布されていたので、試しにIE10入りのWindows7Ubuntuにインストールしようとしてみたのですが…

$ wget -i https://az412801.vo.msecnd.net/vhd/IEKitV1_Final/VirtualBox/Linux/IE10_Win7/IE10.Win7.For.LinuxVirtualBox_2.txt
$ ls
IE10.Win7.For.LinuxVirtualBox.part1.sfx  IE10.Win7.For.LinuxVirtualBox.part3.rar  IE10.Win7.For.LinuxVirtualBox.part5.rar
IE10.Win7.For.LinuxVirtualBox.part2.rar  IE10.Win7.For.LinuxVirtualBox.part4.rar  IE10.Win7.For.LinuxVirtualBox_2.txt
$ chmod +x IE10.Win7.For.LinuxVirtualBox.part1.sfx
$ ./IE10.Win7.For.LinuxVirtualBox.part1.sfx
zsh: そのようなファイルやディレクトリはありません: ./IE10.Win7.For.LinuxVirtualBox.part1.sfx

IE10.Win7.For.LinuxVirtualBox.part1.sfxあるのに…

sfxファイルの中身を調べてみると

$ file IE10.Win7.For.LinuxVirtualBox.part1.sfx
IE10.Win7.For.LinuxVirtualBox.part1.sfx: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.8, stripped

32bit用の実行ファイルのようです。 OSが64bitなので、32bitの実行ファイルを使えるようにライブラリをインストールします。

$ sudo apt-get update
$ sudo apt-get install libc6:i386 libstdc++6:i386

で、もう一度試してみると…

$ ./IE10.Win7.For.LinuxVirtualBox.part1.sfx 

RAR SFX archive

Extracting from ./IE10.Win7.For.LinuxVirtualBox.part1.sfx

Extracting  IE10 - Win7.ova                                            0%
…

どうやらうまくいったっぽいです。 あとはできあがったovaファイルをVirtualBoxにインポートすれば仮想マシンが使えるようになります。

参考

ruby-opencvがRuby 2.0で使えるようになりました

というわけで

気がついたらだいぶ時間が開いてしまいましたが、ようやくRuby 2.0に対応しました。

ruby-opencv | RubyGems.org | your community gem host

動作環境は

  • Ruby 1.9.3, 2.0.0
  • OpenCV 2.4.5

となっております。 例によってWindows用にはmingw32,mswin32用のコンパイル済みパッケージも用意してあります。

Ruby 2.0対応以外の変更点は、バグ修正やFat gem作成機能追加ぐらいなので、そろそろ新しい機能も入れたいところですね。 中途半端に手を付けたFaceRecognizerを次回ぐらいで入れたいなー、 などと考えております。あとはリファレンスも進めなければ…。

開発は以下のリポジトリでやっていますので、お気づきの点やご要望、イケてるPull Requestなど、お待ちしております。

ruby-opencv/ruby-opencv - GitHub

VirtualBoxでAndroidしてみる

VirtualBoxAndroidを入れてみたくなったので

調べたらこんなプロジェクトがありました。

AndroVM | Running Android in a Virtual Machine

The "AndroVM" initiative aims at providing a "as good as possible" support to run Android in Virtual environments.

AndroVM – Documentation | AndroVM blog

仮想環境でAndroidがうまく動くようにしよう、というプロジェクトのようです。

VirtualBox用の.ovaファイルが配布されているので、これを使ってインストールしてみます。

環境

ダウンロード

まずは http://androvm.org/blog/download/ からovaファイルをダウンロードします。 何種類かありますが…

Please note AndroVM comes in 3 different versions :

  • vbox86p : Built for a "phone", 480×800 default resolution
  • vbox86t : Built for a "tablet", 1024×600 default resolution
  • vbox86tp : Built for a "tablet" with phone capabilities, 1024×600 default resolution

AndroVM – Documentation | AndroVM blog

  • vbox86p …携帯電話用 / 解像度 480×800
  • vbox86t …タブレット用 / 解像度 1024×600
  • vbox86tp …タブレット用(電話機能付き)/ 解像度 1024×600

という違いのようです。 また、末尾が「with gapps & houdini」となっているものは、Playストアのアプリが同梱されています。 というわけで今回は、vbox86tp version with gapps & houdini の最新版 androVM_vbox86tp_4.1.1_r4-20121119-gapps-houdini-flash.ovaAndroid 4.1.1相当) をダウンロードします。

インストール

VirtualBoxを起動し、メニューから [ファイル]→[仮想アプライアンスのインポート] でダウンロードした.ovaファイルをインポートします。

ドキュメントにはインポート後、仮想マシンのネットワーク設定で「ホストオンリーアダプタ」を設定するように書いてあるのですが、これだけだと外部のネットワークに繋げられないので、NAT接続も追加しておきます。今回は

  • アダプタ 1: NAT
  • アダプタ 2: ホストオンリーアダプタ

という設定にしました。

動かしてみる

VirtualBoxでインポートしたVMを起動し、Googleアカウント等の設定をすると…

f:id:ser1zw:20130205023428p:plain

いい感じに動いています。Playストアからアプリも問題なくインストールできました。 また、Superuserが同梱されているので、Android Terminal Emulatorなどの端末エミュレータを入れれば、そのまま $ su - でrootになれます。

ボタン操作は?

Some special (hardware) keys :

To unlock screen, press the "Home" or "F1" key The android "back" button is mapped to the physical "Esc" key

AndroVM – Documentation | AndroVM blog

スクリーンのアンロックは Homeキー または F1キー、バックボタンは Escキーにマッピングされています。 ホームボタンも Homeキー にマッピングされているようです。

Kindleアプリは…書籍がダウンロードできない

Kindleストアで購入した書籍をダウンロードしようとすると、ダウンロード終了間際に「問題が発生したため、Amazon Kindleを終了します」 というエラーメッセージが出て、ダウンロードに失敗します。Kindleアプリは十分に使えなさそうな雰囲気です。

まとめ

というわけで微妙に動かないアプリもありますが、割と簡単にVirtualBoxAndroidをインストールすることができました。 ノーリスクでroot権限付きAndroidが手に入るので、Androidで遊ぶにはちょうど良さそうです。

ruby-opencvをようやくRubyGems.orgに登録しました

というわけで

ようやく $ gem install ruby-opencv するだけでruby-opencvがインストールできるようになりました!

ruby-opencv | RubyGems.org | your community gem host

インストールするには?

必要なもの

  • Ruby 1.8.7 または 1.9.3
  • OpenCV 2.4.3

Linux/Macの場合

  1. OpenCVをインストール
  2. 以下のコマンドでruby-opencvをインストール
$ gem install ruby-opencv -- --with-opencv-dir=/path/to/opencvdir

/path/to/opencvdirには、OpenCVがインストールされているディレクトリを指定してください(指定しない場合は /usr/local を見に行きます)。

Windowsの場合

Windows版はmingw32とmswin32用のコンパイル済みパッケージが用意されています。

  1. OpenCVをインストール
  2. OpenCVのdllが格納されたフォルダにパスを通す(例えばOpenCVをC:\opencvにインストールした場合、mswin32ならC:\opencv\build\x86\vc10\bin, mingw32ならC:\opencv\build\x86\mingw\binにパスを通してください)
  3. 以下のコマンドでruby-opencvをインストール
$ gem install ruby-opencv

今後の展開とか

とりあえず

  • Ruby 2.0だと動かないっぽいので直す(ruby-opencv v0.0.8時点)
  • さっさとリファレンスを作る(一応、documentationブランチで作業中)
  • OpenCV 2.xの機能に対応させる

あたりを中心に進めたいと思っています。

動かない!とかこんな機能が欲しい!とかコードがイケてない!など、お気づきの点がございましたら、 ぜひともGitHubのIssuesページよりお知らせください。Pull Requestも大歓迎ですよー。

ruby-opencv/ruby-opencv - GitHub