macportsからHomebrewに乗り換えてMySQL5.1をインストールするまでの流れ
macportsで入れたアプリをlaunchctlからアンロードする
$ sudo launchctl list | grep port 79 - org.macports.mysql5 80 - org.macports.apache2
macportsで作られたplistは以下のディレクトリにある
- /opt/local/etc/LaunchDaemons
アンロード
$ sudo launchctl unload ./org.macports.apache2/org.macports.apache2.plist $ sudo launchctl unload ./org.macports.mysql5/org.macports.mysql5.plist
Homebrewのインストール
$ ruby -e "$(curl -fsSkL https://raw.github.com/mxcl/homebrew/go)"
- オフィシャルだと、curl先が、raw.github.com だけど、これだと繋がらなかったので https://raw.github.com に変更
Xcodeコマンドラインツールのインストール
インストール時に入れろと警告が出たのでインストール
- https://developer.apple.com/downloads/index.action
- 最新版だと、Xcodeの中からダウンロードできたらしい
Xcodeのアップデート
その後こっちも警告が出たのでAppStoreから最新のを取得
/etc/path の修正
ローカルに入っているアプリの方が、Homebrewより優先されるので、/etc/pathを以下のように修正
- Homebrewの/usr/local/binを先頭にする
$ cat /etc/paths
/usr/local/bin
/usr/bin
/bin
/usr/sbin
/sbin
.zshrcを修正してもできる
gitのインストール
Homebrewのシステムでgitを使っているので絶対に必要
$ brew install git
Homebrewの更新
$ brew update
MySQL5.1のインストール
#標準だと5.5系がインストールされるので、他のバージョンを入れるformulaを追加 $ brew tap homebrew/versions # MySQL5.1をインストール $ brew install mysql51
この後、インストール時の説明にしたがって、launchctlをロードするが
そのままだと、手動でMySQLを終了できなくなるので、.plistファイルを編集する必要がある
# $ mkdir -p ~/Library/LaunchAgents $ cp /usr/local/Cellar/mysql51/5.1.67/com.mysql.mysqld.plist ~/Library/LaunchAgents/ $ launchctl load -w ~/Library/LaunchAgents/com.mysql.mysqld.plist $ launchctl list | grep sql - 1 com.mysql.mysqld # 初回設定 $ mysql_install_db --user=mysql --tmpdir=/tmp # ルートパスワードの設定 $ /usr/local/Cellar/mysql51/5.1.67/bin/mysqladmin -u root password '*****' # ログインしてみる $ mysql -u root -p
- tapは、formulaを追加するコマンド
- いろんなサイトで、MySQL5.1を入れるときは、「brew install https://raw.github.com/adamv/homebrew-alt/master/versions/mysql51.rb」とあるけど、今は404になっているので使えない
- オフィシャル側で用意してくれているのをtapする
- brew versionsで出てくるMySQL5.1.56は、取得先にファイルが無いためにエラーになるので使わない
ログを出すようにする
$ mkdir /var/log/mysql $ sudo chown kasei_san /var/log/mysql $ vim /usr/local/var/mysql/my.cnf
[mysqld] log-error=/var/log/mysql/error.log log=/var/log/mysql/query.log
ログローテーションは、newsyslogでできるらしい。そのうちやる?
その他メモ
再起動
mysql.server restart
my.cnfの場所
/usr/local/var/mysql/my.cnf
クライアント起動時に特定のデータベースにアクセス
$ mysql -u root -p -D test
参考
- 本家
- いろんなバージョンのformulaが置いてある所
- Homebrew解説
- tapについて
- PATHの話
そんなですー
jsのプロトタイプについてまとめてみた
なんか理解がぼんやりしていたのでまとめてみました
JavaScriptはプロトタイプベースっていうけど、それって何?
プロトタイプベースとは、全てのオブジェクトは他のオブジェクトのクローンを派生させたものという設計のオブジェクト指向。派生元をプロトタイプという
プロトタイプベースでは、自身が定義していない要素にアクセスした場合、プロトタイプをたどって要素を探しに行く。これをプロトタイプチェーンと言う
Javaみたいなクラスベースとの違いは?
クラスベースでは、全てのオブジェクトは静的なクラスから生成される。
その為ちょっとしたクラスの派生を作りたい場合にも、クラス新たに定義する必要があるし、また基本的には動的にクラスを定義できない
プロトタイプベースでは、オブジェクトに要素を自由に追加出来るので、柔軟なのが利点
JavaScriptではどうやって、オブジェクトを生成するの?
JavaScriptでは、オブジェクトの生成は基本的にはnew演算子で行う
var a = new A();
new演算子がやることは、
- 引数に渡した関数オブジェクト(コンストラクタ)が持つプロトタイプを引き継いだオブジェクトを生成する事
- それをthisとして関数オブジェクトを実行すること
あと、newで生成されたオブジェクトをインスタンスと呼ぶ。
- instanceof演算子で、どの関数オブジェクトから生成されたのか調べる事が可能
newしたり、生成されるオブジェクトがインスタンスだったり、クラスベースっぽいね
newやinstanceof演算子の為に、クラスベースっぽく見えるけど、JavaScriptはれっきとしたプロトタイプベースの言語。
クラスベースっぽく見えるのは、ネットスケープがJavaScriptを公開する時にJavaサーブレットも同時に公開する為に、JavaScriptもJavaっぽくしたかったというマーケティング的な理由があったからと思われる
プロトタイプっぽいところは?
JavaScriptでは、prototype要素にプロトタイプの情報を格納する。
prototype要素は関数オブジェクト生成時に自動的に、constructor要素にその関数オブジェクトを入れたものが生成される
ちなみに、newを実行した時には、そのconstructor要素が呼ばれるので、中身を差し替える事で、呼ばれるコンストラクタを動的に変更できる。キモい!
var A=function(name){ this.name = name; } A.prototype.say = 'hello'; var a = new A('Bob'); a.name; // Bob a.say; // hello
プロトタイプはインスタンスが個別に持つのではなく、new した関数オブジェクトのprototypeを参照するので、newした後の変更も反映される
A.prototype.say = 'hi!'; a.say; // hi!
プロトタイプチェーンを使って継承っぽいことができるらしいけど?
web上ではこんなやり方が多い
var Asub = function(){...} Asub.prototype = new A(); Asub.prototype.constructor = Asub; // prototypeを上書きしたので、コンストラクタを再定義 Asub.prototype.add = function{ ... } // 追加したい要素を定義していく
継承元の関数オブジェクトをnewしたインスタンスをプロトタイプに入れる
Aの持つ要素を全てAsubのプロトタイプに入れるイメージ
コンストラクタで定義された要素もプロトタイプに入るから漏れがないように感じる
だけど、色々欠点がある
例えば、こんなことしたい場合
// 面積 var A = function(width, height){ this.width = width; this.height = height; } A.prototype.area = function(){ return this.width*this.height; } // 体積 var Asub = function(width, height, depth){...} Asub.prototype.volume = function{ return this.width*this.height*this.depth; } // これだと、A側のコンストラクタを引数付きで実行できない! Asub.prototype = new A();
なので、サイ本とかだと以下の方法をおすすめしている
// applyで、継承元のコンストラクタをコンストラクタで実行する var Asub = function(width, height, depth){ A.apply(this, [width, height]); } // Object.createは、引数のプロトタイプを持つオブジェクトを生成するメソッド Asub.prototype = Object.create(A.prototype); Asub.prototype.constructor = ASub;
コンストラクタ側で、継承元のコンストラクタも呼ぶ
んで、newするのではなく、Object.createでprototypeを定義する
これで、継承元のコンストラクタと、prototypeをごっちゃにせずに引き継ぎすることが可能
そんな感じです
画像のレスポンスコードが304の場合、onloadイベントが発生しない件と対策
以下のようなコードを書くと、画像が304を返した場合に、onloadイベントが発生しない
var img = new Image(); img.onload = function(){ console.log("loaded!"); } img.src = "./01.jpg"; // 01.jpg が304を返すと、onloadが発生しない!!
当然と言えば当然なのですが、少しハマったので対策をメモ
先に対策
setIntervalで img.complete をチェックし続ける
img.completeは、画像が読み込み完了したら true を返す
- キャッシュであれ、webから取ってくるのであれ、読み込みが終わったらtrueになるので、304でも読み込み完了を知ることができる
onloadと、img.completeの動き
MacのFirefox16, Safari5.1, chrome23で動作確認した結果はこんな感じ
- onloadは200の時にのみイベントが発生
- img.completeはなんであれ、サーバから結果を受け取ったらtrueがかえってくるっぽい
- ブラウザのキャッシュの場合は未調査
- 後でやる
サンプルコード
rack でこんなの書いて実験した
def self.call(env) data = nil File.open("./01.jpg", "rb") do |f| data = f.read end case env['PATH_INFO'] when '/200.jpg' [200, {"Content-Type" => "image/jpeg"}, [data]] when '/304.jpg' [304, {}, [data]] when '/500.jpg' [500, {}, [data]] when '/404.jpg' [404, {"Content-Type" => "image/jpeg"}, []] when '/' [200, {"Content-Type" => "text/html"}, [(<<-EOS)]] <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <script> var img_check = function(path){ var img = new Image(); img.onload = function(){console.log(path + ":onload");} img.src = path; var id = setInterval(function(){ console.log(path + ":img.complete:" + img.complete); if(img.complete){ clearInterval(id); } }, 100); }; img_check("/200.jpg"); // onloadイベントが発生して、complete:true img_check("/304.jpg"); // onloadイベントが発生しなくて、complete:true img_check("/500.jpg"); // onloadイベントが発生しなくて、complete:true img_check("/404.jpg"); // onloadイベントが発生しなくて、complete:true </script> </head> <body> </body> </html> EOS end end run self
そんな感じ
追記
onerrorも試してみたら、304もonerrorが発生してた
MacPortsで、mecabと、mecab-rubyのインストール
色々手間だったので記録
mecab のインストール
$ sudo port install mecab
入った
$ port installed | grep mecab mecab @0.994_0+ipadic (active) mecab-base @0.994_0 (active) mecab-ipadic @2.7.0-20070801_0 (active)
$ mecab -v
mecab of 0.994
mecab-ruby のインストール
mecab-rubyは、ruby にバインドする必要があるらしいので、
gem install だとできないし、rvmなので、macportsからも
インストールできない
参考 → rvmで入れた各rubyにmecab rubyバインディングをインストールする - 橋本詳解
上記を参考にインストール
$ wget http://mecab.googlecode.com/files/mecab-ruby-0.994.tar.gz .
解凍
$ tar -zxvf mecab-ruby-0.994.tar.gz $ cd mecab-ruby-0.994/ $ rvm list rvm rubies => ruby-1.8.7-p174 [ x86_64 ] ruby-1.9.2-p180 [ x86_64 ]
とりあえず、1.8.7 に入れる
READMEにやり方が書いてあったので、続きはそちらを参照
$ cat README MeCab ruby module $Id: README,v 1.1.1.1 2005/12/03 14:18:52 taku-ku Exp $; 1. Installation % ruby extconf.rb % make % su # make install 2. How to use? See 'test.rb' as a sample program.
$ ruby extconf.rb checking for main() in -lmecab... yes checking for main() in -lstdc++... yes checking for mecab.h... yes creating Makefile $ make g++ -I. -I. -I/Users/kobayashi/.rvm/rubies/ruby-1.8.7-p174/lib/ruby/1.8/i686-darwin10.7.0 -I. -DHAVE_MECAB_H -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -fno-common -g -O2 -fno-common -pipe -fno-common -I/opt/local/include -c MeCab_wrap.cpp cc -dynamic -bundle -undefined suppress -flat_namespace -o MeCab.bundle MeCab_wrap.o -L. -L/Users/kobayashi/.rvm/rubies/ruby-1.8.7-p174/lib -L. -lruby -lstdc++ -lmecab -ldl -lobjc $ sudo make install Password: /usr/bin/install -c -m 0755 MeCab.bundle /Users/kobayashi/.rvm/rubies/ruby-1.8.7-p174/lib/ruby/site_ruby/1.8/i686-darwin10.7.0
なんかあっさり終わった。
できたのかね?
サンプルコード動かしてみる
$KCODE = 'u' require 'MeCab' m = MeCab::Tagger.new("-Ochasen") print m.parse("今日もしないとね")
dyld: lazy symbol binding failed: Symbol not found: __ZN5MeCab12getLastErrorEv Referenced from: /Users/kobayashi/.rvm/rubies/ruby-1.8.7-p174/lib/ruby/site_ruby/1.8/i686-darwin10.7.0/MeCab.bundle Expected in: flat namespace dyld: Symbol not found: __ZN5MeCab12getLastErrorEv Referenced from: /Users/kobayashi/.rvm/rubies/ruby-1.8.7-p174/lib/ruby/site_ruby/1.8/i686-darwin10.7.0/MeCab.bundle Expected in: flat namespace
んー???
mecab-rubyをmacportsのライブラリにリンクするように修正
otool -L でリンクしているライブラリを調査できるらしい
otool -L MeCab.bundle MeCab.bundle: /Users/kobayashi/.rvm/rubies/ruby-1.8.7-p174/lib/libruby.dylib (compatibility version 1.8.0, current version 1.8.7) /usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.9.0) /usr/lib/libmecab.1.dylib (compatibility version 2.0.0, current version 2.0.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.11) /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 227.0.0)
MacPortsで入れたなら、/opt/local/lib/libmecab.2.dylib とかになるはず…
元々あるライブラリにリンクされてる??
事例があった → mecab-rubyをMacにインストールするメモ | インサイドフラッギング
extconf.rb をいじるらしい
require 'mkmf' mecab_config = with_config('mecab-config', 'mecab-config') use_mecab_config = enable_config('mecab-config') `mecab-config --libs-only-l`.chomp.split.each { | lib | have_library(lib) } $CFLAGS += ' ' + `#{mecab_config} --cflags`.chomp have_header('mecab.h') && create_makefile('MeCab')
使用するライブラリのディレクトリを指定するために、$LDFLAGSを追加
require 'mkmf' mecab_config = with_config('mecab-config', 'mecab-config') use_mecab_config = enable_config('mecab-config') $LDFLAGS += ' -L' + `#{mecab_config} --libs-only-L`.chomp `mecab-config --libs-only-l`.chomp.split.each { | lib | have_library(lib) } $CFLAGS += ' ' + `#{mecab_config} --cflags`.chomp have_header('mecab.h') && create_makefile('MeCab')
もっかいインストール
$ make clean $ ruby extconf.rb checking for main() in -lmecab... yes checking for main() in -lstdc++... yes checking for mecab.h... yes creating Makefile $ make g++ -I. -I. -I/Users/kobayashi/.rvm/rubies/ruby-1.8.7-p174/lib/ruby/1.8/i686-darwin10.7.0 -I. -DHAVE_MECAB_H -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -fno-common -g -O2 -fno-common -pipe -fno-common -I/opt/local/include -c MeCab_wrap.cpp cc -dynamic -bundle -undefined suppress -flat_namespace -o MeCab.bundle MeCab_wrap.o -L. -L/Users/kobayashi/.rvm/rubies/ruby-1.8.7-p174/lib -L. -L/opt/local/lib -lruby -lstdc++ -lmecab -ldl -lobjc $ sudo make install Password: /usr/bin/install -c -m 0755 MeCab.bundle /Users/kobayashi/.rvm/rubies/ruby-1.8.7-p174/lib/ruby/site_ruby/1.8/i686-darwin10.7.0
もっかいサンプルコード実行したところ、激しく文字化け
ä» \¿\«\à\é ä» Ì¾»ì-°ìÈÌ Šæ—\もしだŠæ—\もしだŠæ—\もしだµ­¹æ-°ìÈÌ ªã ªã ªã ̾»ì-¸Çͭ̾»ì-ÁÈ¿\ „とね „とね „とね µ­¹æ-°ìÈÌ EOS
辞書ライブラリをutf-8のものを参照するように修正
辞書ライブラリが原因らしい
/opt/local/etc/mecabrc
; ; Configuration file of MeCab ; ; $Id: mecabrc.in,v 1.3 2006/05/29 15:36:08 taku-ku Exp $; ; dicdir = /opt/local/lib/mecab/dic/sysdic ; userdic = /home/foo/bar/user.dic ; output-format-type = wakati ; input-buffer-size = 8192 ; node-format = %m\n ; bos-format = %S\n ; eos-format = EOS\n
/opt/local/lib/mecab/dic/sysdic なる辞書を使っているらしいので見てみると…
$ ll /opt/local/lib/mecab/dic total 8 drwxr-xr-x 11 root admin 374 12 12 12:10 ipadic-eucjp/ lrwxr-xr-x 1 root admin 12 6 9 2012 sysdic@ -> ipadic-eucjp
なんかeucとか言ってる
そら化ける
本当は、mecabのvariantsにutf-8があるから、それを入れるのが正解だったっぽい
仕方ないので以下を参考に、utf-8の辞書を別途インストール
$ sudo port install mecab-ipadic-utf8
入った
port installed | grep mecab mecab @0.994_0+ipadic (active) mecab-base @0.994_0 (active) mecab-ipadic @2.7.0-20070801_0 (active) mecab-ipadic-utf8 @2.7.0-20070801_0 (active)
辞書が追加されたことを確認
ll /opt/local/lib/mecab/dic total 8 drwxr-xr-x 11 root admin 374 12 12 12:10 ipadic-eucjp/ drwxr-xr-x 11 root admin 374 12 12 13:38 ipadic-utf8/ lrwxr-xr-x 1 root admin 12 6 9 2012 sysdic@ -> ipadic-eucjp
シンボリックリンクを貼り直す
$ sudo rm sysdic $ sudo ln -s ipadic-utf8/ sysdic $ ll total 8 drwxr-xr-x 11 root admin 374 12 12 12:10 ipadic-eucjp/ drwxr-xr-x 11 root admin 374 12 12 13:38 ipadic-utf8/ lrwxr-xr-x 1 root admin 12 12 12 13:44 sysdic@ -> ipadic-utf8/
もう一度サンプルコード実行
今日 キョウ 今日 名詞-副詞可能 も モ も 助詞-係助詞 し シ する 動詞-自立 サ変・スル 未然形 ない ナイ ない 助動詞 特殊・ナイ 基本形 と ト と 助詞-接続助詞 ね ネ ね 助詞-終助詞 EOS
おおお、動いた!
形態素解析結果の取得方法
$KCODE = 'u' require 'MeCab' m = MeCab::Tagger.new("-Ochasen") node = m.parseToNode("今日もしないとね") while node do puts "#{node.surface} : #{node.feature}" node = node.next end
インクリメントに取得していくのには、parseToNode で取得して、nextで次を取得
surfaceで、分割された単語を返して、featureで情報を取得
あと、最初と最後にヘッダとフッタみたいのが付く
こんな感じ
: BOS/EOS,*,*,*,*,*,*,*,* 今日 : 名詞,副詞可能,*,*,*,*,今日,キョウ,キョー も : 助詞,係助詞,*,*,*,*,も,モ,モ し : 動詞,自立,*,*,サ変・スル,未然形,する,シ,シ ない : 助動詞,*,*,*,特殊・ナイ,基本形,ない,ナイ,ナイ と : 助詞,接続助詞,*,*,*,*,と,ト,ト ね : 助詞,終助詞,*,*,*,*,ね,ネ,ネ : BOS/EOS,*,*,*,*,*,*,*,*
eachとかできないのに違和感があるので、eachできるようにしている人が居た
MySQLで、やたら重いクエリが発生したので、原因を究明するまでの話
今後の為に原因の追求方法をメモ
まずは、どのクエリが重いのか調査
SHOW PROCESSLIST
実行中のプロセスが表示される
処理時間がTimeに表示されたり、実行中のコマンドがInfoに表示されるので、重い処理がどれだか判る
ちなみに、KILLコマンドで、処理中のプロセスを殺すことも可能
EXPLAIN SELECT でどんな風に検索されているか確認
今回は、indexがきちんと使われているか、確認の為に使用
+----+-------------+------------+-------+---------------+------------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+-------+---------------+------------+---------+------+------+-------------+ | 1 | SIMPLE | test_table | index | NULL | test_index | 4 | NULL | 10 | Using where | +----+-------------+------------+-------+---------------+------------+---------+------+------+-------------+
- typeがindexならば、indexが使われている
- typeがALLならば、フルテーブルスキャン(重い)
MySQL :: MySQL 4.1 リファレンスマニュアル :: 5.2.1 EXPLAIN 構文(SELECT に関する情報の取得)
indexが使われているが、やたら重いのでボトルネックを確認する
SHOW PROFILE でクエリの処理毎の実行時間を見る
やたら重い箇所があったらそこがボトルネックなはず
mysql> SET profiling = 1; mysql> SELECT ... mysql> SHOW PROFILE; +--------------------------------+------------+ | Status | Duration | +--------------------------------+------------+ | starting | 0.000081 | | checking query cache for query | 0.000484 | | Opening tables | 0.000052 | | System lock | 0.000020 | | Table lock | 0.000092 | | init | 0.000283 | | optimizing | 0.000137 | | statistics | 0.000052 | | preparing | 0.000089 | | executing | 0.000002 | | Sorting result | 0.000010 | | Sending data | 115.337027 | | end | 0.000066 | | query end | 0.000015 | | freeing items | 0.000201 | | storing result in query cache | 0.000022 | | logging slow query | 0.000001 | | logging slow query | 0.033929 | | cleaning up | 0.000034 | +--------------------------------+------------+
Sending dataは、読み込みと絞りこみに掛かってる時間
This is quite a misleading status. It should be called "reading and filtering data".
MySQL :: MySQL 5.1 リファレンスマニュアル (オンラインヘルプ) :: 8.5.5.33 SHOW PROFILES 構文
普通に、検索結果をwhereするのに時間が掛かってる様子
この辺でMySQLのファイルが上手くキャッシュメモリに乗っていないのではないか? という疑惑が
vmstatコマンド で、disk ioを調査
disk io 等を調査できるlinuxのコマンド
詳しくはこっち → linuxで空きメモリがどんくらいあるか確認する方法 - かせいさんとこ
$ vmstat -SM 10 procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------ r b swpd free buff cache si so bi bo in cs us sy id wa st 0 0 361 5 1 267 0 0 0 2 987 85 0 0 100 0 0 〜 検索開始 〜 0 1 361 5 1 267 0 0 348 0 1024 90 1 2 55 43 0 0 1 361 5 1 267 0 0 610 3 1128 192 0 3 0 97 0
SELECT文発行直後に、ディスクioが大量に発生して、さらにcpuのio待ち率が97%とかになってる!
調べてみたら、テーブル用のファイルがOSのディスクキャッシュより大きかったために、SELECT文を発行する度にHDDにアクセスしていたのが原因だった
今回は、テスト用の環境なので、データ量を減らす事で解決
ハマりました...
おまけ
MySQLのtable_cacheは、テーブルそのもののキャッシュではなく、テーブルに必要なファイルのハンドラをキャッシュする領域
テーブルそのもののキャッシュと勘違いしてました...
linuxで空きメモリがどんくらいあるか確認する方法
freeコマンドを見る
$ free -m total used free shared buffers cached Mem: 497 380 116 0 2 145 -/+ buffers/cache: 233 264 Swap: 511 349 162
-mオプションは、容量をメガバイト単位で表示するオプション
Memが実メモリで、Swapが仮想メモリ
- total : メモリの総量
- used : 使用したメモリ寮
- shard : 共有メモリ → プロセス間で共有できる領域用のメモリらしい
- buffers : バッファに使われているメモリ
- cached : ページキャッシュに使われているメモリ
バッファって?
バッファ → 緩衝器、緩衝帯の意
I/Oアクセスする時に、直接I/Oに行くのではなく、キャッシュ経由でアクセスさせる為のメモリ
データが来ているのに受け手が処理できないので、使うはずのデータを貯めておくらしい
ページキャッシュって?
キャッシュ → 隠し場所、貯蔵品
ページキャッシュ → ファイルのデータを入れておいて、再度アクセスがあった時に素早く返す為の領域
1度アクセスがあったファイルは、再度使われる可能性が高いので保持している
また、linuxはメモリに空きがある限りページキャッシュを蓄えるので、Memのfreeが少ないからといって、メモリが足りない訳ではない
アプリにて実メモリが必要になったら、ページキャッシュは破棄されて、アプリにメモリが割り当てられるはず
実際の空き領域はどうやってみるの?
空き領域をチェックするのに必要なのは、上の "-/+ buffers/cache:"
これは、バッファ、キャッシュメモリを引いた値を出していて、使用可能な空き領域を示している
Swapが使われているってことは、メモリが足りないの?
一概にそうとは言えない
linux側で使用していないアプリのメモリをSwapに移して、ページキャッシュに割り当てる場合があるので
それじゃあメモリが足りてるかどうかはどうやって見るの?
まずは、"-/+ buffers/cache"の freeがあからさまに少ない場合
後は、スラッシングが発生しているかチェックする
スラッシング?
メモリ不足の為、Swapと実メモリの行き来が大量に発生して、処理が遅延する現象
vmstatコマンドでチェック可能
$ vmstat -SM 10 procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------ r b swpd free buff cache si so bi bo in cs us sy id wa st 0 0 361 5 1 267 0 0 0 2 987 85 0 0 100 0 0 0 1 361 5 1 267 0 0 348 0 1024 90 1 2 55 43 0 0 1 361 5 1 267 0 0 610 3 1128 192 0 3 0 97 0
-SMは、単位をメガバイトにしてるオプション
10 は、10秒毎に表示するオプション
各表示項目の意味は、以下を参照
ここで、si, so が多発していたらスラッシングが発生している
メモリが足りなかったら?
メモリを足すか、アプリがメモリを使わないように修正するか
頑張れ
Phantom.js+QUnitでjavascriptの自動テスト環境を作りたい
Phantom.jsって何?
PhantomJS is a headless WebKit with JavaScript API.
- [PhantomJS: Headless WebKit with JavaScript API ](http://phantomjs.org/)
他に特徴は?
色んなテストフレームワークに対応
jasmine, capybara, QUnit, mocha, webdriver, yui test, busterjs, funcunit, robot framework
他のCUIブラウザと比べて軽い
ブラウザの動作をエミュレートしている訳ではない為
導入
自分の環境は、MacPortsなので、手動でインストール
$ wget http://phantomjs.googlecode.com/files/phantomjs-1.7.0-macosx.zip $ unzip phantomjs-1.7.0-macosx.zip $ cd phantomjs-1.7.0-macosx $ bin/phantomjs -v 1.7.0
簡単。
jsを使って動かす
まずは、hello world
console.log('hello, world!'); // メインスレッドが終わっても終了しないので、 // 明示的に終わらせる必要がある phantom.exit();
$ bin/phantomjs test.js Hello, world!
ページを読み込む
var page = require('webpage').create(); page.open('http://google.com', function () { // ページのキャプチャ page.render('google.png'); phantom.exit(); });
標準だと、594x442px でレンダリングされてる
page.viewportSizeでサイズの変更が可能
page.viewportSize = { width: 480, height: 800 };
内部でjavascriptも動くの?
onloadで、div id="test" に、文字列を表示する、htmlを用意して実験
<html> <head> <meta charset="utf-8"> <title>title</title> <script> window.onload = function(){ document.getelementbyid("test").innerhtml='test'; } </script> </head> <body> <div id="test1"></div> </body> </html>
test.js にて、htmlを読み込んで、onloadで表示されるdiv id="test"の中の文字列を取得して表示する
var page = require('webpage').create(); page.open('01.html', function () { // ページアクセス setTimeout(function(){ console.log( // ページ内でJSを動かすメソッド page.evaluate(function () { return document.getElementById("test").innerHTML; }) ); phantom.exit(); }, 1000); });
$ bin/phantomjs test.js test
うごいた!
イベント送受信
今度はこんなHTMLを用意
div id="test" をクリックすると、confirmが出て分岐
// 〜 中略 〜 window.onload = function(){ document.getElementById("test").addEventListener("click", function(){ if(confirm("click?")){ alert("yes"); }else{ alert("no"); } }); } </script> </head> <body> <div id="test">click me!</div> </body> </html>
var page = require('webpage').create(); page.open('01.html', function () { // ページアクセス page.onConfirm = function(msg){ // confirm発生時にトリガ console.log("confirm : "+msg); return true; // 真偽値でOK、キャンセルを決める } page.onAlert = function(msg){ // アラート発生時にトリガ console.log("alert : "+msg); } page.includeJs("jquery.min.js", function(){ // jQueryをinclude var offset = page.evaluate(function(){ // リンクのoffsetを取得 return $("* test").offset(); }); page.sendEvent('click', offset.left+1, offset.top+1); // クリック }); });
$ bin/phantomjs test.js alert : jquery.min.js * includeJs完了時に、alertイベントが飛んでるっぽい confirm : click? alert : yes
イベントを受け取って、confirmでyesを選択している
ちなみに、confirmを受け取らない場合、デフォルトでは「いいえ」を選択している様子
QUnit連携
- javascriptのunitテストフレームワーク
- Qunitを、Phantom.js で実行させることが可能
- 全てCUIで実行できるので、CI環境に持ち込むのが簡単
連携には、examples/run-QUnit.js を使う
試しに、QUnitのテストをPhantom.jsで実行してみる
$ examples/run-QUnit.js ../jquery-QUnit-4e03a4b/test/index.html; echo $1 'waitfor()' finished in 1901ms. tests completed in 1771 milliseconds. 596 tests of 596 passed, 0 failed.bin/phantomjs 0
- 成功の場合、戻り値は0
- 失敗の場合は、1を返す
まとめ
- 内部的には、QUnitをopenして、結果を出力するDOMをウォッチしているだけなので、ちょっといじれば、エラーが発生したテストを出力するとかできそう
- 戻り値を返すので、Jenkinsへの組み込みも簡単にできそう
- 全ブラウザのテストは無理だけど、CIに組み込めるのが良いと思った
- デザイナさんがjavascript書いて、テストもQUnitで書くという状況ならば、Phantom.js と組み合わせて、CIに組み込むのがベター?