em-http-requestで非同期httpダウンロード

1月 18th, 2012

rubyにはem-http-requestという非同期httpクライアントがあるらしいので
使ってみた。

gemでem-http-requestのインストール

$ gem install em-http-request

download.rb


#!/usr/bin/env ruby
# -*- coding: utf-8 -*-

require ‘pathname’
require ‘rubygems’
require ‘eventmachine’
require ’em-http’

class Downloader
def download(fetch_list_file)
pending = File.open(fetch_list_file).each{}.lineno
abort “Stop Downloader, because fetch list is empty.” if pending <= 0 download_dir = "#{Pathname.new(ENV["HOME"])}/Downloads" Dir.mkdir(download_dir) unless File.exists?(download_dir) EM.run do File.open(fetch_list_file) {|f| f.each do |url| url.chomp! http = EM::HttpRequest.new(url).get http.callback { puts "==> Fetched `#{url}'”
filename = File.basename(url)
if http.response_header.status == 200 then
begin
File.open(“#{download_dir}/#{filename}”, File::WRONLY|File::CREAT|File::TRUNC|File::NONBLOCK) { |f|
f.puts http.response
puts “==> Overwriting `#{download_dir}/#{filename}'”
}
rescue => ex
puts “Failed to open file : file=[#{download_dir}/#{filename}}] : reason=[#{ex.message}]”
end

else
puts “Failed request : url=[#{url}] : status=[#{http.response_header.status}]”
end
pending -= 1
EM.stop if pending < 1 } http.errback { puts "#{url} : #{http.error}" pending -= 1 EM.stop if pending < 1 } end } end end end def main(fetch_list_file) if fetch_list_file == nil || !File.exist?(fetch_list_file) then abort "set first arg as `fetch list file!'" end downloader = Downloader.new downloader.download(fetch_list_file) end if __FILE__ == $0 main(ARGV[0]) end [/ruby] URLのリストファイルを読み込んでfetchする実装にしてみた。

$ cat fetch_list.txt
http://logsoku.com/thread/ikura.2ch.net/news/1322798773/
http://guide.jp.real.com/moviecollection/synopsis_27528.htm
http://guide.jp.real.com/moviecollection/synopsis_27526.htm
http://guide.jp.real.com/moviecollection/synopsis_27520.htm
http://guide.jp.real.com/moviecollection/synopsis_27502.htm

$ ruby ./download.rb fetch_list.txt
==> Fetched `http://guide.jp.real.com/moviecollection/synopsis_27528.htm’
==> Overwriting `/Users/hmatsuda/Downloads/synopsis_27528.htm’
==> Fetched `http://guide.jp.real.com/moviecollection/synopsis_27526.htm’
==> Overwriting `/Users/hmatsuda/Downloads/synopsis_27526.htm’
==> Fetched `http://guide.jp.real.com/moviecollection/synopsis_27520.htm’
==> Overwriting `/Users/hmatsuda/Downloads/synopsis_27520.htm’
==> Fetched `http://guide.jp.real.com/moviecollection/synopsis_27502.htm’
==> Overwriting `/Users/hmatsuda/Downloads/synopsis_27502.htm’
==> Fetched `http://logsoku.com/thread/ikura.2ch.net/news/1322798773/’
==> Overwriting `/Users/hmatsuda/Downloads/1322798773′

SuperVisorでイベント監視(crashmail編)

11月 30th, 2011

SuperVisorの基本は前回の記事を参照してください。

SuperVisorにおけるイベント監視のメカニズム

supervisordが検知したサブプロセスのイベントを、
事前に定義しておいたeventlistenerが処理する。
eventlistenerは自作する事ができるし、
よくありがちな処理をするeventlistenerはsuperlanceというpythonパッケージで提供されている。

公式のEventsドキュメントを読む限りでは様々なイベントの補足ができるようだ。
イベントの種類

– PROCESS_STATE_STARTING | プロセスが起動準備状態になった
– PROCESS_STATE_RUNNGING | プロセスが起動した
– PROCESS_STATE_BACKOFF | プロセスの起動に失敗した
– PROCESS_STATE_STOPPING | プロセスが停止準備状態になった
– PROCESS_STATE_STOPPED | プロセスが正常に停止した
– PROCESS_STATE_EXITED | プロセスがcrashした
– PROCESS_STATE_FATAL | プロセスの起動を指定されたretry回数分試みたが失敗した
– PROCESS_STATE_UNKNOWN | supervisordにエラーが発生した
– PROCESS_LOG | プロセスがログファイルを吐いた
– PROCESS_COMMUNICATION_STDOUT | プロセスが標準出力の支持を出した
– PROCESS_COMMUNICATION_STDERR | プロセスが標準エラー出力の支持を出した
– SUPERVISOR_STATE_CHANGE_STARTING | supervisordが起動準備状態になった
– SUPERVISOR_STATE_CHANGE_STOPPING | supervisordが停止準備状態になった
– TICK_5 | 毎5秒ごとに発生するイベント
– TICK_60 | 毎分ごとに発生するイベント
– TICK_3600 | 毎時ごとに発生するイベント

今回はapacheがcrashした時にメールで通知したかったため、
eventlistenerを自作しようと思っていたがEventsのドキュメントを見ていて
superlanceというpluginパッケージがあることを発見。
ドキュメントを読むとsuperlanceには下記のeventlistenerをパッケージングしているとのこと。
httpok
TICK_系のイベントを監視し定期的にhttpサーバの死活チェックを行い、request失敗やtimeoutの場合再起動してメールするplugin
crashmail
プロセスが死んだらメールするplugin
memmon
プロセスが閾値以上のメモリを使っていたらrestartしてメールするplugin

crashmailが今回の要件にマッチしてそうなのでこれを使うことにした。

superlanceのインストール

supervisor同様にeasy_installでインストールできる。

$ sudo easy_install superlance

crashmailの設定

eventlistenerは通常のデーモンと同じように定義します、つまりeventlistnerもデーモン化されるということです。
詳しくは設定ファイルのリファレンスを参照。

通常のデーモンはprogramと記述するところをeventlistenerに変更する。
eventsという設定が必要で、監視するイベントを定義する。(コンマ区切りで複数指定可)

$ sudo vi /etc/supervisord.conf
[eventlistener:crashmail]
command=/usr/local/bin/crashmail -p apache2 -m dev@example.com
events=PROCESS_STATE_EXITED

今回の設定ではapacheがcrashした時にdev@example.comにアラートメールを送信する事ができる。

crashmailをsupervisordの監視下に追加する

supervisorctlコマンドでeventlistenerを追加してみましょう。

$ sudo /usr/local/bin/supervisorctl update
$ sudo /usr/local/bin/supervisorctl status
apache2 RUNNING pid 50197, uptime 2:00:06
crashmail RUNNING pid 48783, uptime 0:00:01

statusコマンドの実行結果でcrashmailがRUNNINGになっていれば成功です。

crashmailの動作テスト

今回はapache2がcrashした場合のテストをしたいため、httpdをkillallしてみる。

$ sudo killall -9 httpd

killallするとhttpdが一度死んでsupervisorによって蘇ります。
さらにcrashmailがhttpd死亡イベントを検知して以下のようなメールをdev@example.comに
送っていれば正常に動作したことになります。

subject: apache2 crashed at 2011-11-30 11:58:23,804
to: dev@example.com
body:
Process apache2 in group apache2 exited unexpectedly (pid 12536) from state RUNNING

※PROCESS_STATE_FATALやPROCESS_STATE_BACKOFFも補足してくれるのかソースを読んでみたが、どうやらPROCESS_STATE_EXITEDをハードコードしているので補足したい場合は自前でeventlistenerを作成する必要がある。
crashmail.py

    def runforever(self, test=False):
        while 1:
            # we explicitly use self.stdin, self.stdout, and self.stderr
            # instead of sys.* so we can unit test this code
            headers, payload = childutils.listener.wait(self.stdin, self.stdout)

            if not headers['eventname'] == 'PROCESS_STATE_EXITED':
                # do nothing with non-TICK events
                childutils.listener.ok(self.stdout)
                if test:
                    self.stderr.write('non-exited event\n')
                    self.stderr.flush()
                    break
                continue

SuperVisor導入(基本編)

11月 29th, 2011

SuperVisorとは

SuperVisorはデーモンプロセスの管理をしてくれるデーモンです。
僕が知っている似たようなデーモンとしてdaemontoolsがあります。
SuperVisorはlinuxやmacで動作します。

便利な機能
– プロセスクラッシュ時に自動で再起動
– ログの管理
– イベントの監視ができる (デーモンがcrashした時にメール通知などが簡単にできる)
– 設定ファイルがシンプル(1ファイルでok)
– 標準でwebI/Fが付属されている

インストール

SuperVisorはpythonで書かれているのでpythonが必要です。
※pythonのインストール方法は割愛します。

pythonにはeasy_installというお手軽インストーラパッケージがあり、このコマンドを使って
SuperVisorをインストールすることができます。

# easy_installをインストール
$ wget http://peak.telecommunity.com/dist/ez_setup.py
$ chmod 755 ./ez_setup.py
$ sudo python ./ez_setup.py

# supervisorをインストール
$ sudo easy_install supervisor

成功すると下記コマンドがシステムにインストールされるはずです。

supervisord // supervisor本体
supervisorctl // supervisor管理コマンド
echo_supervisor_conf // 設定ファイル生成script

設定

シンプルに設定ファイル1つで行えます。
※apacheのようにincludeコマンドを使って複数の設定ファイルに分けることも可能です。
詳しくは設定ファイルのリファレンスを参照。

echo_supervisor_confは設定例を標準出力してくれるコマンドなので利用して生成します。

$ sudo sh -c “echo_supervisor_conf > /etc/supervisord.conf”

必要最低限の設定を記述します。

$ sudo vi /etc/supervisord.conf
[unix_http_server]
file=/tmp/supervisor.sock ; (the path to the socket file)

[supervisord]
logfile=/tmp/supervisord.log ; (main log file;default $CWD/supervisord.log)
logfile_maxbytes=50MB ; (max main logfile bytes b4 rotation;default 50MB)
logfile_backups=10 ; (num of main logfile rotation backups;default 10)
loglevel=info ; (log level;default info; others: debug,warn,trace)
pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
nodaemon=false ; (start in foreground if true;default false)
minfds=1024 ; (min. avail startup file descriptors;default 1024)
minprocs=200 ; (min. avail process descriptors;default 200)

[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[program:apache2]
command=/usr/sbin/httpd -c “ErrorLog /dev/stdout” -DFOREGROUND
redirect_stderr=true
stdout_logfile=/var/log/apache2/log.out
stderr_logfile=/var/log/apache2/log.err

これはapacheをデーモン化する例です。
[progoram:YYY]のようにprogramブロックを追加していけばどんどん管理するデーモンを増やせます。

supervisord起動

それでは設定も終わったことですしsupervisordを起動してみましょう。

$ sudo /usr/local/bin/supervisord
$ sudo /usr/local/bin/supervisorctl status
apache2 RUNNING pid 12104, uptime 0:00:02

statusコマンドの実行結果でapache2がRUNNINGになっていれば成功です。

運用

SuperVisor管理下のプロセスの制御は全てsupervisorctlコマンドで行う事になります。

起動

$ sudo supervisorctl start apache2

再起動

$ sudo supervisorctl restart apache2

停止

$ sudo supervisorctl stop apache2

サブプロセスの設定変更を反映
下記コマンドを実行すれば反映可能。
ただし、全てのサブプロセスが再起動してしまう。

$ sudo kill -HUP `cat /tmp/supervisord.pid`

ちょっと面倒くさいが下記コマンドを実行すれば指定のプロセスのみ
設定を反映して再起動できる。

$ sudo supervisorctl update apache2
$ sudo supervisorctl stop apache2
$ sudo supervisorctl remove apache2
$ sudo supervisorctl add apache2

コマンドヘルプ
コマンドの詳細が知りたければhelpを実行する。
公式サイトにもコマンドの詳細は載ってなかった。

$ sudo supervisorctl help start

nginxでFastCGIはplackupでやると超簡単

12月 19th, 2010

ちょっとnginxでFastCGIを使ってみたくなってnginx with fastCGIのドキュメントを読んでみた。
spawn-fcgiを使ってがんばるみたいなドキュメントしかなくてよくわからない。
そういえばplackでできたようなと思ってcpanを調べてみるとそれらしきモジュールがあった。

1. まずはドキュメント通りにnginx設定ファイルを用意。
nginx.conf

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    server {
        location / {
               set $script "";
               set $path_info $uri;
               fastcgi_pass   unix:/tmp/fcgi.sock;
               fastcgi_param  SCRIPT_NAME      $script;
               fastcgi_param  PATH_INFO        $path_info;
               fastcgi_param  QUERY_STRING     $query_string;
               fastcgi_param  REQUEST_METHOD   $request_method;
               fastcgi_param  CONTENT_TYPE     $content_type;
               fastcgi_param  CONTENT_LENGTH   $content_length;
               fastcgi_param  REQUEST_URI      $request_uri;
               fastcgi_param  SEREVR_PROTOCOL  $server_protocol;
               fastcgi_param  REMOTE_ADDR      $remote_addr;
               fastcgi_param  REMOTE_PORT      $remote_port;
               fastcgi_param  SERVER_ADDR      $server_addr;
               fastcgi_param  SERVER_PORT      $server_port;
               fastcgi_param  SERVER_NAME      $server_name;
        }
    }
}

2. FastCGIスクリプトを用意。(ドキュメントのコードにlistenするport or socketと立ち上げるプロセス数をオプションで取れるようにしただけ)
hello.psgi

use strict;
use warnings;
use Getopt::Long;
use Plack::Handler::FCGI;

my $port_or_sock = "/tmp/fcgi.sock";
my $num_proc = 1;
GetOptions("listen=s" => \$port_or_sock,
           "nproc=i" => \$num_proc);

my $app = sub {
    return [ 200,
             ['Content-Type' => 'text/html'],
             ["Hello, World!!!"]
           ];
};

my $server = Plack::Handler::FCGI->new(
      nproc  => $num_proc,
      listen => [ $port_or_sock ],
      detach => 1,
);

$server->run($app);

3. nginxを起動する

$ sudo /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf

4. FastCGIスクリプトをplackupで起動する

$ plackup -s FCGI --listen /tmp/fcgi.sock --daemonize \
                --nproc 32 /usr/local/nginx/fcgi/hello.psgi

たったこれだけでhttp://localhost/を見るとHello, World!!!が表示される。
うーむ、FastCGIの知識ゼロでここまで簡単に使えちゃうとはPlack恐るべし。

mysqlのCREATE TABLEで存在しないENGINEを指定した場合の挙動

10月 5th, 2010

存在しないENGINE名を指定するとwarningが出るがデフォルトとして指定されている
ENGINEで作成される。

mysql$ CREATE TABLE t (a varchar(32), PRIMARY KEY (a) ) ENGINE = this_does_not_exist;
Query OK, 0 rows affected, 2 warnings (0.07 sec)

mysql$ SHOW WARNINGS\G
*************************** 1. row ***************************
Level: Warning
Code: 1286
Message: Unknown table engine ‘does_not_exist’
*************************** 2. row ***************************
Level: Warning
Code: 1266
Message: Using storage engine MyISAM for table ‘t’
2 rows in set (0.01 sec)

vmware fusion

9月 5th, 2009

OSクリーンインストールしたから仮想マシンも全部入れ直し。と思ってたけど、

~/Documents/Virtual Machines.localized

をまるごとコピーするだけで見事に動いた。
vmware素敵。

snow leopardクリーンインストールメモ

8月 30th, 2009

leopard -> snow leopardへのクリーンインストールを行うため、
time capsuleからのリストアもせずに、アプリケーションもクリーンインストールする。

install後にtimecapsuleからリストアするもの
homeディレクトリ
/usr/local以下
firefoxとthunderbirdのprofile

OSのクリーンインストール
この手順で行う。

Xcodeをインストール
10.6のインストールdiskに入っているので入れる。

tigerのバンドルをインストール
10.4のインストールdiskからバンドルアプリを入れる。
さらに最新版にアップデートしておく。

Appインストール
iusethisの登録アプリを全部入れる

firefox + thunderbird

このサイトの方の方法でバックアップしてみる

macportも入れ直す
$port installed
The following ports are currently installed:
apache2 @2.2.11_0+darwin_9 (active)
apr @1.2.12_1+darwin_9
apr @1.3.7_0 (active)
apr-util @1.3.8_0 (active)
atk @1.24.0_0 (active)
autoconf @2.63_0 (active)
autoconf213 @2.13_1 (active)
automake @1.11_0 (active)
boehmgc @7.0_1+darwin_9 (active)
boost @1.34.1_3+darwin_9
boost @1.35.0_2+darwin_9 (active)
boost-jam @3.1.16_0 (active)
bzip2 @1.0.5_1 (active)
cairo @1.8.0_0+macosx (active)
curl @7.19.0_0 (active)
cyrus-sasl2 @2.1.22_0+kerberos (active)
db46 @4.6.21_5 (active)
emacs @22.2_0 (active)
emacs-w3m @1.4.4_0 (active)
expat @2.0.1_0 (active)
fontconfig @2.5.0_0+macosx (active)
freetype @2.3.5_1 (active)
gawk @3.1.6_0 (active)
gettext @0.17_3 (active)
git-core @1.6.3.3_0+doc+gitweb+svn (active)
glib2 @2.16.3_0+darwin_9 (active)
gmake @3.81_0 (active)
gnupg @1.4.9_0 (active)
gperf @3.0.3_0 (active)
gsed @4.2_0 (active)
help2man @1.36.4_1 (active)
jasper @1.900.1_1+darwin_9 (active)
jpeg @6b_2 (active)
libexif @0.6.16_1 (active)
libgdiplus @1.2.6_1 (active)
libiconv @1.12_0 (active)
libmcrypt @2.5.8_1 (active)
libpixman @0.12.0_1 (active)
libpng @1.2.26_0 (active)
libtool @2.2.6a_0 (active)
libungif @4.1.4_2 (active)
libxml2 @2.7.3_0 (active)
libxslt @1.1.24_2 (active)
log4cpp @1.0_0 (active)
lv @4.51_2 (active)
lynx @2.8.6rel.5_1+ssl (active)
m4 @1.4.13_0 (active)
mhash @0.9.9.9_0 (active)
mono @1.9_0+darwin_9 (active)
mysql5 @5.0.83_0 (active)
mysql5-server @5.0.83_0 (active)
mysqlxx @3.0.8_0+mysql5 (active)
ncftp @3.2.1_0 (active)
ncurses @5.6_0 (active)
ncursesw @5.6_1 (active)
neon @0.26.4_1 (active)
nkf @2.0.8b_0 (active)
openssl @0.9.8g_0 (active)
p5-compress-raw-zlib @2.015_0 (active)
p5-compress-zlib @2.015_0 (active)
p5-crypt-ssleay @0.57_0 (active)
p5-error @0.17015_0 (active)
p5-html-parser @3.60_0 (active)
p5-html-tagset @3.20_0 (active)
p5-io-compress-base @2.015_0 (active)
p5-io-compress-zlib @2.015_0 (active)
p5-libwww-perl @5.826_0 (active)
p5-locale-gettext @1.05_0 (active)
p5-perl-tidy @20071205_0 (active)
p5-svn-simple @0.27_0 (active)
p5-term-readkey @2.30_0 (active)
p5-uri @1.37_0 (active)
pcre @7.8_1 (active)
perl5.8 @5.8.8_3+darwin_9 (active)
php5 @5.3.0_1+apache2+macosx (active)
pkgconfig @0.23_0
pkgconfig @0.23_1 (active)
popt @1.15_0 (active)
readline @5.2.007_0+darwin_9 (active)
render @0.9_1 (active)
rsync @3.0.6_0 (active)
serf @0.3.0_0 (active)
sqlite3 @3.5.8_0 (active)
subversion @1.6.3_0 (active)
subversion-perlbindings @1.6.3_0 (active)
tiff @3.8.2_1+macosx (active)
tree @1.5.1.1_0 (active)
w3m @0.5.2_0 (active)
wget @1.11.1_0 (active)
Xft2 @2.1.12_0 (active)
xorg-util-macros @1.1.5_0 (active)
xorg-xproto @7.0.11_1 (active)
xrender @0.9.0_2 (active)
m4 @1.4.13_0 (active)
mhash @0.9.9.9_0 (active)
mono @1.9_0+darwin_9 (active)
mysql5 @5.0.83_0 (active)
mysql5-server @5.0.83_0 (active)
mysqlxx @3.0.8_0+mysql5 (active)
ncftp @3.2.1_0 (active)
ncurses @5.6_0 (active)
ncursesw @5.6_1 (active)
neon @0.26.4_1 (active)
nkf @2.0.8b_0 (active)
openssl @0.9.8g_0 (active)
p5-compress-raw-zlib @2.015_0 (active)
p5-compress-zlib @2.015_0 (active)
p5-crypt-ssleay @0.57_0 (active)
p5-error @0.17015_0 (active)
p5-html-parser @3.60_0 (active)
p5-html-tagset @3.20_0 (active)
p5-io-compress-base @2.015_0 (active)
p5-io-compress-zlib @2.015_0 (active)
p5-libwww-perl @5.826_0 (active)
p5-locale-gettext @1.05_0 (active)
p5-perl-tidy @20071205_0 (active)
p5-svn-simple @0.27_0 (active)
p5-term-readkey @2.30_0 (active)
p5-uri @1.37_0 (active)
pcre @7.8_1 (active)
perl5.8 @5.8.8_3+darwin_9 (active)
php5 @5.3.0_1+apache2+macosx (active)
pkgconfig @0.23_0
pkgconfig @0.23_1 (active)
popt @1.15_0 (active)
readline @5.2.007_0+darwin_9 (active)
render @0.9_1 (active)
rsync @3.0.6_0 (active)
serf @0.3.0_0 (active)
sqlite3 @3.5.8_0 (active)
subversion @1.6.3_0 (active)
subversion-perlbindings @1.6.3_0 (active)
tiff @3.8.2_1+macosx (active)
tree @1.5.1.1_0 (active)
w3m @0.5.2_0 (active)
wget @1.11.1_0 (active)
Xft2 @2.1.12_0 (active)
xorg-util-macros @1.1.5_0 (active)
xorg-xproto @7.0.11_1 (active)
xrender @0.9.0_2 (active)
zlib @1.2.3_1 (active)