2009年12月22日

ApacheのアクセスログをMessagePack形式で出力するためのモジュールを作りました

register to: はてなブックマークに登録 | del.icio.usに登録 | この記事をクリップ!

Apacheモジュールのログ出力、こんどはMessagePack版を作成しました。続いてはこちらをご紹介します。

Apacheのアクセスログを使い、ユーザアクセスの集計やパターン解析などというのは一般にどこでもやられていることだと思います。通常のアクセスログはテキストファイルなので、集計を行うためにスクリプト上で扱える変数・オブジェクト化が必要になりますね。1行ごとの各ログ項目を取り出すのに正規表現を使ったり、cutやawkなどを使い空白で分割するなど、色々工夫されていることと思います。

今回、MessagePack版のアクセスログ出力をやってみようと思い立ったのは、アクセスログをあらかじめ構造化済みの状態で保存しておければ、読み込みの際の解析する手間を省くことで解析処理の高速化が期待できるのではないか、そう考えたためです。MessagePackであれば、PythonやRubyはじめ様々なスクリプト言語でのバインディングを備えており、取り扱いも簡単ですから、ファイル読み込みからログデータの取り出しまでを速やかに行い、その分集計処理の実装に集中することができるわけです。また、時折イレギュラー的に長かったり、予期しない文字がリクエストに入る等したためにログ解析の正規表現がエラーになったりすることがありますが、構造化済みの形式で保存するのであれば、この問題も回避できます。

何より、元々構造化されたデータがアクセスログ上では一度文字列になり、これを解析する時に再 度解釈処理されるという二度手間になっているわけで、テキストデータ化せずデータ構造を保った バイナリとしておくのはこの手間を回避する目的も持っています。

ソースはこちらです。
http://log.blog.klab.org/support/mod_log_msgpack/mod_log_msgpack-0.1.0.tar.gz

***

それでは、実際にどのような変更を加えたのでしょうか。モジュールの中身を見てみましょう。

mod_log_msgpackは、従来型のログ出力モジュールであるmod_log_configからその多くを受け継いでいます。mod_log_configの構造がそのまま、mod_log_msgpackにも当てはまるとお考えください。

mod_log_configは、起動直後にLogFormatで定義された書式文字列を読み込み、アクセスログに出力する一行分の内容を定義します。例えば、

LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" combined

このようなログ書式の指定があったとします。mod_log_configは、このディレクティブを解釈して下記のような内部的なデータ構造に分解して落とすのです。http: //httpd.apache.org/docs/2.2/ja/mod/mod_log_config.html#formats を参考にしながら、当てはめてみるとこうなります。

(mod_log_config)
%hリモートホスト(を出力する関数をコールバック)
" "空白
%l(identdからもし提供されていれば)リモートログ名
" "空白
%uリモートユーザ
" "空白
%tリクエストを受け付けた時刻
" \""空白+「"」(ダブルクオテーション)
%rリクエストの最初の行
"\" "「"」+空白
%>s最後のステータス
" "空白
%bレスポンスのバイト数。HTTPヘッダは除く。
" \""空白+「"」(ダブルクオテーション)
%{Referer}iReferer:ヘッダの内容
"\" \""「"」+空白+「"」
%{User-agent}iUser-agent:ヘッダの内容
"\""「"」

このように、%?の箇所とそれ以外(空白等、置き換えない文字列)とでそれぞれ区切り、小片に分けられます。%で始まる項目は対応するコールバック関数を通し、指定のリクエスト情報を文字列に変換します。つまり、リクエスト情報は一旦LogFormatの項目数分(今回18個)の部分文字列に変換され、最後にこれらをすべて連結してログ1行分として出力しています。

mod_log_msgpackではこの部分を利用、アクセスログ1行をMessagePackのarray1個として置き換えて下記の項目のみ保持することとしました。

(mod_log_msgpack)
%hリモートホスト(を出力する関数をコールバック)
%l(identdからもし提供されていれば)リモートログ名
%uリモートユーザ
%tリクエストを受け付けた時刻
%rリクエストの最初の行
%>s最後のステータス
%bレスポンスのバイト数。HTTPヘッダは除く。
%{Referer}iReferer:ヘッダの内容
%{User-agent}iUser-agent:ヘッダの内容

つまり、用があるのは%?の項目のみということにして、それ以外の固定文字列に関しては無視して います。%rや%iについていた「"」での囲みも、項目の内容が空白を含みうることを想定して区切 りとしての空白と区別がつかなくなることを回避するためのものなので、arrayとして分割できて あれば、不要なものとなるわけです。

さらに、これらのコールバック関数はrequest_rec構造体(と、%{…}?となったときの{}の中身)を引数にとり、部分文字列を返値とするわけですが、msgpack_packer構造体を渡す引数を1つ追加、ここにログの内容を順次packしていく処理に切り替えています。

static const char *log_remote_host(request_rec *r, char *a)
{
    return ap_escape_logitem(r->pool, ap_get_remote_host(r->connection,
                                                         r->per_dir_config,
                                                         REMOTE_NAME, NULL));
}

こちらが元々のmod_log_config版。"%h"に対応するコールバックです。mod_log_msgpackになりま すと下のように変更になりました。

static int _strpack(const char* s, msgpack_packer* pk)
{
    if (s) {
        size_t n = strlen(s);
        msgpack_pack_raw(pk, n);
        msgpack_pack_raw_body(pk, s, n);
    } else {
        msgpack_pack_nil(pk);
    }
    return 0;
}

static int log_remote_host(request_rec *r, char *a, msgpack_packer* pk)
{
    const char* str = ap_escape_logitem(r->pool, ap_get_remote_host(r->connection,
                                        r->per_dir_config,
                                        REMOTE_NAME, NULL));
    return _strpack(str, pk);
}

log_remot_host()の第3引数が追加され、msgpack_pack_xxxの関数が呼び出されていることが確 認できると思います。

あとはここまでログ内容を集めたmsgpack_packer構造体を、ファイルに書き出す関数 apr_file_write()に渡して書き出しを行います。渡す際、mod_log_msgpackであれば行っている部分文字列の連結の箇所がありますが、mod_log_msgpackでは不要なので省略しています。

static apr_status_t ap_default_log_writer( request_rec *r,
                           void *handle,
                           const char **strs,
                           int *strl,
                           int nelts,
                           apr_size_t len)

{
    char *str;
    char *s;
    int i;
    apr_status_t rv;

#if 0
    // ここから、部分文字列の連結になるので
    str = apr_palloc(r->pool, len + 1);

    for (i = 0, s = str; i < nelts; ++i) {
        memcpy(s, strs[i], strl[i]);
        s += strl[i];
    }
    // ここまでスキップ。
#else
    str = *strs;
#endif

    rv = apr_file_write((apr_file_t*)handle, str, &len);

    return rv;
}
***

おおよその変更の概要は以上です。あとは、mod_log_configとの併存を可能にするためにいくつか細かい変更を加えていますが、次回以降に続くということで^^;。


klab_gijutsu2 at 17:39
この記事のURLComments(0)TrackBack(0)
2009年10月22日

Apacheのアクセスログをsyslog経由で出力するためのモジュールを作りました

register to: はてなブックマークに登録 | del.icio.usに登録 | この記事をクリップ!

皆さんは、負荷分散環境でのApacheのアクセスログをどのように取り扱ってますか?

通常、Apacheのログは動作サーバ上のローカルファイルとして出力されるので、 Webサーバを同時に何台も稼働させて負荷分散を行うような環境では、それらすべ てのWebサーバのログファイルを集めなければなりません。ローカルファイルとし て出力されるということは、Webサーバの台数分だけログファイルがばらけること を意味します。考えるだけでめんどくさいですね。

KLabでは、このApacheログを2パターンを使い分けて集めています。ひとつは syslogによるリモート出力を使い、全Webサーバからのログ出力を一か所に集中さ せる方法です。これは、CustomLogディレクティブにloggerコマンドを使用するこ とで可能です。


    CustomLog "|/usr/bin/logger -p local6.info --" (書式文字列)

これにより、通常ファイル出力されるアクセスログをloggerコマンド経由でsyslog に渡すことができます。あとはsyslogの設定で、リモートのログ集積サーバに集め させるというわけです。

ただし、これには問題もあります。syslogのログ転送はUDPやバッファリングなど の関係で取りこぼしがどうしても発生してしまう点です。いくつか対策は考えられ ますが、私たちはこれに従来型のファイル出力のログを併用することで解決してい ます。これが2パターンの2つめで、すなわち従来のように各Webサーバ上で出力す るログファイルを日次バッチで集積するという方法です。詳細なログ解析などはこ の日次の集積バッチによるログファイルを使用し、その日その日の緊急のアクセス 確認などはsyslog出力の側を見る、という使い分けを行っているのです。

***

さて、このsyslog出力について、もう一度CustomLogを確認します。loggerコマン ドへのパイプを用いていますので、httpdの子プロセスとしてloggerプロセスが多 数立ち上がることになります。

しかしふと考えてみると、syslogに送るって言ってもやることはsyslog()関数を 呼ぶくらい(他にもopenlog()とかありますが)なので、わざわざ外部プロセスにし なくても、Apache本体から出力できたっていいよね、と考えたわけです。

ということで、Apacheから直接syslogにアクセスログを出力するためのApacheモ ジュールを一つ作ってみました。

  • mod_syslog-0.1.0.tar.gz
  • syslogに送る設定は、以下のようにCustomLogを変更して行います。

    
        CustomLog syslog:foo (書式文字列)
    
    

    "syslog:"をプレフィックスとして付加することで、mod_syslogがsyslogへの出力 であることを判別して処理を行いますが、パイプやファイルパス形式、つまり "syslog:"が付かない場合は引き続き従来と同じ処理が行われます。

    ***

    mod_syslogのやることは至ってシンプルです。Apacheは、ログ出力処理のための関 数2つを下に表わすように定義しています。

      static ap_log_writer_init* ap_log_set_writer_init(ap_log_writer_init *handle);
      static ap_log_writer* ap_log_set_writer(ap_log_writer *handle);
    

    ap_log_set_writer_init()は、ログ出力のための初期化処理(ファイルのオープン など)を行うための関数、またap_log_set_writer()は、実際のログ出力を行う際に 呼ばれる関数をそれぞれコールバック登録します。あとは、Apacheがそれらのコー ルバック関数を必要なタイミングで呼び出してくれます。

    さらに上記の関数2つは返値を持ち、それぞれのコールバック関数の事前の値を表し ます。mod_syslogは"syslog:"のプレフィックス以外のログ出力先、すなわちパイプ やファイル出力については従来の処理に任せますので、mod_syslog自身のコールバッ ク関数登録と同時に従来のコールバック関数へのポインタを、この返値によって取得、保持します。

    #define PREFIX_SYSLOG   "syslog:"
    #define PREFIX_SYSLOG_LENGTH    7
    
    static void *
    ap_syslog_writer_init(apr_pool_t *p, server_rec *s,
                          const char* name)
    {
        syslog(LOG_DEBUG, "%s: prev_log_writer_init = %p, name = %s", __func__, prev_log_writer_init, name);
    
        if (strncasecmp(PREFIX_SYSLOG, name, PREFIX_SYSLOG_LENGTH) == 0) {
            return &dummy[0];
            // NULL以外を返す。
            // 同じ値(&dummy[0])かどうかをap_syslog_writer()関数で判別できればよい。
        }
    
        if (prev_log_writer_init) {
            return prev_log_writer_init(p, s, name);
        }
    
        return NULL;
    }
    
    // ap_hook_pre_config()でフック登録
    static int
    syslog_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp)
    {
        ...
    
        if (!prev_log_writer_init) {
            void* f;
    
            f = ap_log_set_writer_init(ap_syslog_writer_init);
            if (f != ap_syslog_writer_init) {
                prev_log_writer_init = f;
            }
            f = ap_log_set_writer(ap_syslog_writer); // ap_syslog_writerの内容については省略
            if (f != ap_syslog_writer) {
                prev_log_writer = f;
            }
        }
    
        return OK;
    }
    

    つまり、上記の例で言えばprev_log_writer_initとprev_log_writerに従来の処理 へのコールバック関数のポインタが入るわけです。 なお、この2関数が返値を返すようになったのはApache2.2以降なので、Apache2.0 系ではそのままでは使用できません。一応、同名の関数から旧い値を返させるだけ で動作するところまでは確認していますが、もしお試しの際はご注意ください。

    ***

    このように、Apacheからアクセスログを出力するにあたり、モジュールの追加だけ でも結構な機能追加ができることが分かります。しかも、添付の例をご覧のように、 わずかなコード量で実現できています。あったらいいな、こんなことできたらうれ しいな、というちょっとした発想を柔軟に取り入れられるApacheの仕組みにはやは り目を見張るものがありますね。


    klab_gijutsu2 at 16:56
    この記事のURLapache | 運用
    2009年10月05日

    Windows用ソフトウェア「イージスガード」を公開します

    register to: はてなブックマークに登録 | del.icio.usに登録 | この記事をクリップ!

    「イージスガード」は Windows PC 上のデータをウイルスやマルウェアの脅威から守るためのソフトウェアです。KLab は個人向けにこのソフトウェアの無償公開を開始しました。

    イージスガード
    ダウンロードはこちらから
    イージスガード公式ページ(KLab サイトへ)


    イージスガードには次の機能・特長があります。

    • 所定のフォルダ・ファイルを保護対象として設定可能
    • 許可しないプログラムからのアクセスを報告・記録する
    • 許可しないプログラムからのアクセスを透過的にダミーフォルダへ誘導
    • PC 環境に応じて保護設定を自動生成する
    • 改変されたプログラムからのアクセスを抑制する
    • 有効なデジタル署名つきのプログラムには自動的に許可を与える

    イージスガードは、以前このブログでご紹介した「WinAmulet」を母体とする上位互換のソフトウェアです。WinAmulet ユーザ各位のご支援のおかげで、多くの機能を追加し大きく性能を改善したこの新しいソフトウェアが生まれました。もちろん、WinAmulet の保護設定をインポートすることも可能です。

    イージスガードに使用制限はありません。多くの方に自由にご利用頂くことで、悲惨な情報流出被害発生の防止につながれば、と願っています。


    (tanabe)

    klab_gijutsu2 at 22:22
    この記事のURLComments(0)TrackBack(1)win 
    2009年09月28日

    Bazaar 2.0 がリリースされました

    register to: はてなブックマークに登録 | del.icio.usに登録 | この記事をクリップ!

    過去に Bazaarの紹介 で紹介したときには 1.9 だったBazaarですが、その後も毎月のリリースをかさねて先週とうとう メジャーバージョンアップとなる 2.0 がリリースされました。(ダウンロードサイト)

    正式なリリースノートは公式Webサイトを参照してもらうとして、この一年弱で改良された点を私の基準で紹介したいと思います。

    TortoiseBZRが(たぶん)実用レベルになった

    私はたいていコマンドラインから利用しているので、以前紹介したときはTortoiseBZRをあまり使い込んでいませんでした。

    その後、TortoiseBZRを使い込んでみたところ、pushができない、addもできないなど最低限の機能がそろっていなかったり、ステータスの表示が重くてストレスになったりしたので結局アンインストールしてしまいました。

    その後今年に入ってからTortoiseBZRの開発が停滞してしまい、このままじゃいけないと思って6月後半からTortoiseBZRの開発に参加しました。この2ヶ月で以下のような問題を解決しました。

    • シェル拡張の致命的なバグ(「新しいフォルダを作成」などのボタンが動作しなくなる、Windows Vistaでコンテキストメニューのレイアウトが崩れるなど)が修正されました。
    • ステータスの取得が速くなりました。また、まだステータスを取得していないファイルの場合 '?' マークのステータスアイコンを表示してバックグラウンドでステータスを取得することで、エクスプローラの応答速度の低下を抑えました。(この方法はTortoiseHGの開発者にTwitterで教えてもらって開発しました。感謝感謝)
    • add, push, send といった機能をコンテキストメニューに追加しました。

    まだTortoiseSVNの完成度には到達していませんが、とりあえずユーザーを邪魔をする挙動が無くなって基本的な操作の大半がコンテキストメニューから呼び出せるようになったので、人に試してみてと言える状態になっています。

    新しいリポジトリフォーマットが標準になった

    Bazaar 2.0 からは 2a といわれる新らしいリポジトリフォーマットが標準になりました。 Bazaarではデータの格納方式が変わる以外にも、新機能のために新しいメタデータを追加した時にも古いバージョンのBazaarからそのメタデータがないリビジョンをコミットされてしまわないようにとリポジトリフォーマットを変更しています。その結果としてBazaarには大量のリポジトリフォーマットができてしまい、「○○のフォーマットでは△△の機能が使えない」といった問題が発生していました。

    新しいフォーマットが標準になることで、リポジトリフォーマットをオプションから選ばなくても新しい機能が全部使える、リポジトリサイズが小さくなったりいくつかの操作が速くなっていたりするという利点があります。

    WindowsでのUnicodeファイル名への対応が改良された

    PythonではUnicode APIを利用することで、Unicodeファイル名でファイルを扱うことができるのですが、 sys.argv で取れるコマンドライン引数は非Unicodeです。(Python3ではUnicodeになります) Bazaarも sys.argv を利用していたため、せっかくUnicodeでファイル名を扱うのにコマンドライン引数からはコードページでしかファイル名を取得できないという問題がありました。

    最近のバージョンではこの部分が GetCommandlineW() APIを利用するようになり、bzr本体だけでなくQBzr等のGUIを含めてUnicodeへの対応が進んでいるので、Unicodeファイル名に関する問題はほとんどなくなりました。

    日本語ユーザーグループができた

    日本語で議論ができる環境が欲しいと思い、Bazaar 2.0 のリリースをきっかけにユーザーグループを作ることにしました。 http://groups.google.co.jp/group/bazaar-ja

    Launchpad上にドキュメント翻訳プロジェクトも作成し、ゆっくりですが翻訳作業も開始しています。TortoiseBZRのバグ報告や要望なんかも、英語でLaunchpadに登録するよりも手軽にできるはずです。Bazaarに関心のある方はぜひ参加してみてください。


    @methane

    klab_gijutsu2 at 21:14
    この記事のURLComments(4)TrackBack(0)Python 
    2009年09月09日

    KLab勉強会#5の資料公開

    register to: はてなブックマークに登録 | del.icio.usに登録 | この記事をクリップ!

    8/31 に開催した、第5回KLab勉強会の資料を公開します。

    『Gainerでアイデアをフィジカルにスケッチする』
      〜 Gainerでアイデアをフィジカルにスケッチする
    『おしゃべりコーヒーボット』
      〜 宇夫 陽次朗 (株式会社IIJイノベーションインスティチュート)
    『hwhack: USB解析のいろいろ』
      〜 宇夫 陽次朗 (株式会社IIJイノベーションインスティチュート)
    『3DCADやRPによるハードウェア開発』
      〜 松村 礼央(株式会社国際電気通信基礎技術研究所(ATR)知能ロボティクス研究所(IRC)環境知能研究室 研修研究員)

    先日の勉強会はちょうど台風の最中でしたが、お越しいただいたきありがとうございました。
    みなさんのおかげで懇親会も楽しく過すことができました。

    勉強会の様子は、動画のほうを準備していますので今暫くお待ちください。


    klab_gijutsu2 at 19:00
    この記事のURLComments(0)TrackBack(0)KLab勉強会 
    2009年08月13日

    「WinAmulet」バージョンアップのお知らせ(2009/08/13)

    register to: はてなブックマークに登録 | del.icio.usに登録 | この記事をクリップ!
    Windows 用フリーソフトウェア「WinAmulet」をバージョンアップしました。
    新しいバージョンは「0.9.4.0」です。

    WinAmulet0.9.4.0 旧バージョンをご利用中の方も、初めての方も、ぜひ最新の WinAmulet をお試し下さい。

    WinAmulet のページ
    ダウンロードはこちらから
    重要なお知らせ!

    ■ このプログラムの概要

    「WinAmulet」は、所定のフォルダに対しアクセス可能なプログラムを制限するソフトウェアです。対象フォルダ下の大切なファイルにユーザの許可しないプログラムがアクセスすることを防ぎます。

    ■ 主な更新内容

    バージョン 0.9.3.0 -> 0.9.4.0 での主な更新内容は以下の通りです。
    • キャッシュ機構の実装により処理効率を改善しました
    • リダイレクト処理の精度を改善しました
    • ファイルオープン要求中の各プロセス名を確認できるようにしました

    (tanabe)
    klab_gijutsu2 at 13:58
    この記事のURLComments(0)TrackBack(0)win 
    2009年07月31日

    KLab勉強会#5開催のお知らせ

    register to: はてなブックマークに登録 | del.icio.usに登録 | この記事をクリップ!

    第5回KLab勉強会のお知らせです。
    今迄のテーマはネットワークに関わることをメインにKLab勉強会を開催してき ましたが、今回は少し趣向を変えハードウェアについて扱っていきます。

    ハードウェアと一口にいっても様々な分野があります。 今回は外部から宇夫陽次朗氏(株式会社IIJイノベーションインスティチュート) と松村礼央氏(株式会社国際電気通信基礎技術研究所)をお招きする予定です。

     宇夫氏からはUSBデバイスの解析方法について、また松村氏からは近い将来 身近な存在になるロボットの開発についてお話を頂きます。

    併せて弊社から、USBデバイスを経由し手軽に工作ができるGainer(ゲイナー)の導入編から事例も交えお話しさせていただきます。

    いずれかのセッションでも興味のあるかたお待ちしております!

    開催要項

    日時
    2009/8/31 (月) 19:00-21:10 (18:30受付開始)
    場所
    KLab株式会社 第3会議室
    東京都港区六本木6-10-1 六本木ヒルズ森タワー20F
    (アクセス方法)
    参加費
    無料
    人数
    30名程度
    (定員になり次第、参加登録を締め切らせていただきます。)

    ※終了後に懇親会(会費制)を予定しています。

    セッション

    1. タイトル
      『Gainerでアイデアをフィジカルにスケッチする』
      講師
      竹井 英行(KLab株式会社)
      概要

      フィジカル・コンピューティングのための入門的なツールキットGainer(ゲイナー)を使って、LEDやアクチュエータなどのハードウェアを簡単なプログラムで動かす方法を説明する。またその中で、Gainerを使いこなすために必要な電気回路の基礎も説明する。その後、応用として、Zigbee+Gainerによるデモンストレーションを 紹介する。

    2. タイトル
      『hwhack: USB解析とかいろいろ』
      講師
      宇夫 陽次朗 (株式会社IIJイノベーションインスティチュート)
      概要

       コンピュータと周辺機器をつなげるためのインターフェイスとしてUSBが広く普及した。PCから組み込み機器といったホスト側や、様々な周辺機器群がUSBインターフェイスを装備し、多くのOSはUSBサポートの機能を標準的に提供している。
       今回はUSBの概略を説明し、USBデバイスの挙動を解析する手法を紹介する。またBSD系OSでのデバイスドライバとの関係も扱っていきたい。

    3. タイトル
      『3DCADやRPによるハードウェア開発』
      講師
      松村 礼央(株式会社国際電気通信基礎技術研究所(ATR)知能ロボティクス研究所(IRC)環境知能研究室 研修研究員)
      概要

       近年、フィジカルコンピューティングをはじめ、インタラクティブな製品の開発が盛んに行われています。そこで今回は,人とコミュニケーションをとるロボットの開発を例に 3DCADやラピッド・プロトタイピング(RP)によるハードウェアの開発方法について概説します。

    参加方法

    register-study-20090831@klab.jp 宛に件名を「参加登録」として、以下の内容のメールを送ってください。

    氏名    :
    所属    :(会社名・学校名等)
    懇親会   :(参加する・しない)
    URL     :ご自身のブログなどがあればそのURLを。(任意)
    コメント  :勉強会についての希望や参加の意気込みなど、
           なんでも結構ですので、よろしければ一言コメ
           ントいただけるとうれしいです。(任意)
    

    ※当日は、受付にて名刺を一枚頂戴いたしますので、予めご用意下さい。


    申し込みを終了致しました。ご応募ありがとうございました。
    参加希望のメールをいただいた方には、順次、当日のご案内をメールでお送り致します。
    万一、参加登録後に都合が悪くなってしまった場合は、当日でもかまいませんのでregister-study-20090831@klab.jpまでご連絡いただけると助かります。

    また、申し込み終了後に参加希望のメールをいただいた方には、定員超過の旨、メールでご連絡いたします。今回は参加いただけませんが、次回の参加をお待ちしております。


    klab_gijutsu2 at 16:17
    この記事のURLComments(0)TrackBack(0)KLab勉強会 
    2009年07月13日

    MessagePackのPython Bindingをリリースしました

    register to: はてなブックマークに登録 | del.icio.usに登録 | この記事をクリップ!

    MessagePack とは、古橋(id:viver)さんが開発された高速・高効率なバイナリシリアライズフォーマットです。詳しくは 古橋さんの日記プロジェクトサイト を見てください。

    PythonからMessagePackフォーマットでSerialize/DeserializeするためのPythonパッケージを作ったので、取得方法と使い方について簡単に紹介します。

    1. インストール

    msgpackという名前でPython Package Index (PyPI)に登録してあります。 <http://pypi.python.org/pypi/msgpack/>

    setuptoolsをインストールしている環境では、

    $ easy_install msgpack
    

    でインストールすることができます。

    Windowsでインストールする場合は、PyPIのパッケージサイト からインストーラをダウンロードしてインストールするのがお手軽です。

    2. シリアライズ

    まず、msgpackパッケージをimportします。

    >>> import msgpack
    >>> help(msgpack)  # docstring を読む
    

    一つのオブジェクトをシリアライズするには、 msgpack.packb() を利用するのが簡単です。

    >>> msgpack.packb([1,2,3])
    '\x93\x01\x02\x03'
    

    連続してシリアライズする場合、 msgpack.Packer オブジェクトを利用するとオーバーヘッドが少なくなります。

    >>> packer = msgpack.Packer()
    >>> packer.pack([1,2,3])
    '\x93\x01\x02\x03'
    >>> packer.pack([4,5,6])
    '\x93\x04\x05\x06'
    

    3. デシリアライズ

    msgpack.unpackb() を利用すると、1オブジェクト分のシリアライズされたデータをデシリアライズできます。

    >>> msgpack.unpackb(b'\x93\x01\x02\x03')
    [1, 2, 3]
    

    ストリームからデシリアライズする場合、 msgpack.Unpacker オブジェクトを利用することで、連続したデシリアライズができたり、オブジェクトの境界が判らない場合に対応できます。

    >>> unpacker = msgpack.Unpacker()
    >>> buf = b'\x93\x01\x02\x03' * 5
    >>> len(buf)
    20
    >>> unpacker.feed(buf[:9])
    >>> for o in unpacker:
    ...     print o
    ...
    [1, 2, 3]
    [1, 2, 3]
    >>> unpacker.feed(buf[9:])
    >>> for o in unpacker:
    ...     print o
    ...
    [1, 2, 3]
    [1, 2, 3]
    [1, 2, 3]
    

    4. ベンチマーク

    テストコード:

    #!/usr/bin/env python
    # coding: utf-8
    
    from msgpack import packs, unpacks
    from cPickle import dumps, loads
    import simplejson as json
    from time import time
    
    BENCH_NUM = 10
    
    def bench(func, num=BENCH_NUM):
        start = time()
        for i in xrange(BENCH_NUM):
                func()
        end = time()
        print "%-12s  %4.3f[ms]" % (func.__name__, (end-start)*1000/BENCH_NUM)
    
    def setup_int():
        global a, a_pickle, a_mpack, a_json
        a = range(1024) * 2**10
        a_pickle = dumps(a)
        a_json = json.dumps(a)
        a_mpack = packs(a)
    
    def setup_str():
        global a, a_pickle, a_mpack, a_json
        a = ['a'*(i % 4096) for i in xrange(2**14)]
        a_pickle = dumps(a)
        a_json = json.dumps(a)
        a_mpack = packs(a)
    
    
    def pickle_dump(): dumps(a)
    def pickle_load(): loads(a_pickle)
    
    def json_dump(): json.dumps(a)
    def json_load(): json.loads(a_json)
    
    def mpack_pack(): packs(a)
    def mpack_unpack(): unpacks(a_mpack)
    
    
    targets = [
            pickle_dump, pickle_load,
            json_dump, json_load,
            mpack_pack, mpack_unpack]
    import gc
    gc.disable()
    
    bytes_suffix = "[bytes]"
    
    print "== Integer =="
    setup_int()
    
    print "= Size ="
    print "pickle:", len(a_pickle), bytes_suffix
    print "json:  ", len(a_json), bytes_suffix
    print "mpack: ", len(a_mpack), bytes_suffix
    
    for t in targets:
        bench(t)
    
    print "== String =="
    setup_str()
    
    print "= Size ="
    print "pickle:", len(a_pickle), bytes_suffix
    print "json:  ", len(a_json), bytes_suffix
    print "mpack: ", len(a_mpack), bytes_suffix
    
    for t in targets:
        bench(t)
    

    結果:

    == Integer ==
    = Size =
    pickle: 6203398 [bytes]
    json:   5154816 [bytes]
    mpack:  2752517 [bytes]
    pickle_dump   248.640[ms]
    pickle_load   360.288[ms]
    json_dump     255.601[ms]
    json_load     136.922[ms]
    mpack_pack    51.383[ms]
    mpack_unpack  26.851[ms]
    == String ==
    = Size =
    pickle: 33731696 [bytes]
    json:   33611776 [bytes]
    mpack:  33595139 [bytes]
    pickle_dump   182.324[ms]
    pickle_load   141.639[ms]
    json_dump     126.675[ms]
    json_load     93.945[ms]
    mpack_pack    66.833[ms]
    mpack_unpack  34.953[ms]
    

    pickleやjsonを圧倒していますが、まだまだ改善の余地がありそうです。 とくにpackはunpackと違って大量のPythonオブジェクトを生成する必要があるわけではないのに、unpackよりも時間がかかってしまっています。

    5. その他

    ここでは紹介していない、simplejsonやpickleモジュールに似せたAPIもあります。pickleやjsonからmsgpackへ移行するときはパッケージの中を探してみて下さい。

    ただし、現在のmsgpackはバイト列・整数・浮動小数点数・シーケンス型・辞書型にしか対応できていません。simplejsonは dumps()defalut() 関数を、 loads()object_hook() 関数を用意することでユーザーが定義したクラスのシリアライズ・デシリアライズができます。0.2 では同じような default()object_hook() へ対応しようと思います。


    @methane

    klab_gijutsu2 at 15:58
    この記事のURLComments(1)TrackBack(1)Python 
    2009年06月24日

    Windowsプログラムの異常終了をトラップするコード

    register to: はてなブックマークに登録 | del.icio.usに登録 | この記事をクリップ!
    ■ はじめに

    先日、社内でこういう話題がありました。
    「Windows 上のプログラム A からソースコードのないプログラム B を起動する必要があるんだけど、プログラム B はときどき異常終了しちゃったりする。内輪用だから落ちること自体は目をつぶるとして、プログラム B が異常終了した場合にはプログラム A 側でそれを上手にハンドルしたい。良い方法はないものか」

    この話に興味を感じ、異常終了を起こす短いプログラムを作ってデバッガでトレースしながらヒントを探している内にふと思いました。


    このプログラムを裸で実行すると、プログラム内で処理されない例外は図のような形でシステムによって処理されます。しかし、デバッガ上でデバッギ(デバッグ対象)として実行している場合はデバッガが例外の発生を検知しそれをユーザに伝えます。つまり、デバッガの制御下にあります。

    ということは「デバッガとして動作するコード」を用意しプログラム B をデバッギとして扱えば、そこで発生した未処理の例外を捕捉することができるかもしれません。 ちょっと面白そうなので試しにそういうプログラムを書いてみることにしました。続きを読む
    klab_gijutsu2 at 10:59
    この記事のURLComments(2)TrackBack(0)win 
    2009年05月25日

    まくおさんバージョンアップのお知らせ(Ver1.2.0)

    register to: はてなブックマークに登録 | del.icio.usに登録 | この記事をクリップ!

    まくおさんをバージョンアップしましたのでお知らせします。
    主な変更点は以下のとおりです。

    【Ver1.1.3→Ver1.2.0】

    • 応答速度と安定性の向上
    • エラーログに"[error]"という文字列を追加
    • メンバがタイムアウトしたときの状況を詳細に出力するようにした
    • msync --statusで、実行中のコマンドを表示するようにした
    • msyncを複数同時実行したときに挙動がおかしくなる不具合を修正
    • ファイルクローズに失敗したときに異常終了する不具合を修正
    • msyncにdeleteオプションを指定したときの結果表示がおかしくなる不具合を修正
    • その他細かい不具合を修正

    【ダウンロード】

    【注意】
    Ver1.2系とVer1.1系はプロトコルに互換性がありませんので、
    バージョンアップは全サーバに対して実施して下さい。

    「ところで、まくおさんってだれ?」という方は、こちらこちらを見てやってください(^^;


    klab_gijutsu2 at 21:23
    この記事のURLComments(0)TrackBack(0)運用 | tool
    Archives
    このブログについて
    DSASとは、KLab が構築し運用しているコンテンツサービス用のLinuxベースのインフラです。現在3ヶ所のデータセンタにて構築し、運用していますが、我々はDSASをより使いやすく、より安全に、そしてより省力で運用できることを目指して、日々改良に勤しんでいます。
    このブログでは、そんな DSAS で使っている技術の紹介や、実験してみた結果の報告、トラブルに巻き込まれた時の経験談など、広く深く、色々な話題を織りまぜて紹介していきたいと思います。

    また、わたしたちが開発したソフトウエアやノウハウ、実験的なサービスを公開している、

    もあわせてご覧いただければと思います。(DSASブログのエントリをシリーズ別に整理した「DSASブログまとめ」もあります)
    KLabについて
    KLab株式会社は、携帯電話の基盤技術から各種ソリューション、コンテンツ企画など多くのサービスを提供している会社です。
    Blog内検索
    QRコード
    QRコード
    最新トラックバック
    [Python]ベンチマーク方法が・・・ (name-3333’s memo (個人用メモなんで間違いがあるかも知れません))
    MessagePackのPython Bindingをリリースしました
    Syndicate this site