KLab若手エンジニアの これなぁに?

KLab株式会社の若手エンジニアによる技術ブログです。phpやRubyなどの汎用LLやJavaScript/actionScript等のクライアントサイドの内容、MySQL等のデータベース、その他フレームワークまで幅広く面白い情報を発信します。

2009年11月

アシアルKLab合同勉強会で発表しました: MessagePackのPHP Extensionについて

こんにちは。takei-hです。 ちょっと時間が経ってしまったのですが、アシアル株式会社KLab株式会社の合同勉強会でMessagePackとPHP Extensionについて発表しましたので、資料を公開します。



また、PHP Extensionもだいたい形になったので、公開します。 MessagePack PHP Extensionのダウンロード ソースを解凍した後、インストールは以下のコマンドで。
$ ./configure
$ make
$ sudo make install
そして、php.iniに以下の行を追加します。
extension=msgpack.so
これで準備完了です。 主に、ruby版のライブラリを参考にしています。使い勝手はPHPのjsonと同じような感じで、msgpack_pack()とmsgpack_unpack()です。 使い方はこんな感じ↓
1), array("takei"=>"hide"), 3);
 var_dump($data);

 // serialize
 $msg = msgpack_pack($data);

 // hexadecimal
 $str = unpack('H*', $msg);
 var_dump("0x".$str[1]);

 // deserialize
 $ret = msgpack_unpack($msg);
 var_dump($ret);
?>
また、ruby版と同様、ストリーミングデシリアライゼーションも可能です。
initialize();
 $buffer = "";
 $nread = 0;

 foreach($msgs as $msg){
    $buffer = $buffer . $msg;

    while(true){
        $nread = $unpacker->execute($buffer, $nread);

        if($unpacker->finished()){
            $msg = $unpacker->data();
            var_dump($msg);

            $unpacker->initialize();
            $buffer = substr($buffer, $nread);
            $nread = 0;

            if(!empty($buffer)){
                continue;
            }
        }
        break;
    }
 }
?>
まだ完全なテストやベンチマークはとっていないのでこれからやろう。。。と思っていたら、本日、MessagePackのPHP Extensionを公開された方がいたようです!(うぅ) MessagePackのPHP Extensionを作りました トロトロしている間に。。でもせっかくなので私も公開させてもらいます笑。優位点としては、array型もpack,unpackできることでしょうか! 世の中の流れは速い!これからはアウトプットを、MessagePackみたいに高速化していきたいですね!! == 追記(2009.11.30 17:34) == GitHubに公開しました。ご指導ご鞭撻のほどよろしくお願いします! http://github.com/hideyuki/messagepack-php-ext

クックパッド様と技術者交流会を行いました

秋の寒さが、木の葉を赤く染める季節になりましたね。みなさんいかがお過ごしでしょうか。 我々若手は、寒さを吹き飛ばすほど熱く燃えております!!


ということで、本日のお昼時、クックパッド様と技術者交流会を行いました。 今回は特にサーバやDBなどのインフラ周りのお話を中心に、楽しい時間を過ごさせていただきました。私は常日頃アプリ側をやっておりますので、今回のインフラ談義はとても勉強になりました!PC向けかモバイル向けか、参照が多いサイトか更新が多いサイトか、ということろで最適なインフラ構成は違ってくるんですね。サーバ/インフラを支える技術 ~スケーラビリティ、ハイパフォーマンス、省力運用を読んでもっと勉強しよう! また、談義中、社員の方が作ってくださったランチを頂きました。ほうれん草のサラダ、牡蠣のグラタン、きのこリゾット。どれもおいしく頂きました♪ごちそうさまでした! そして最後に、素敵なキッチンの前で記念写真。ありがとうございました!


勉強会の共同開催など、今後も積極的に外部との技術交流を図っていきます!

アシアルKLab合同勉強会で発表しました: 携帯絵文字utf-8エンコーディングの話

こんにちは。takada-atです。 先日開催されたアシアル株式会社KLab株式会社の合同勉強会で発表しましたので、資料を公開します。 携帯絵文字の文字エンコーディング変換およびキャリア間のマッピングについてKLabでの対応をご紹介させて頂きました。 資料は以下にあります。 ■携帯絵文字をutf-8エンコーディングにする話

携帯絵文字変換PHPライブラリ PictgramConverter を公開しました

tadaka-atです。 最近Haskellの記事ばかり書いていますが、PHPも書いてます!ということでPHPのライブラリを公開しました。 ■PictgramConverter サポートする機能は
  • 絵文字sjisバイナリをutf-8バイナリに変換
  • キャリア間の絵文字変換
「ShiftJISからutf-8への文字コード変換とともに、絵文字もutf-8エンコーディングに変換」「異なるキャリアの絵文字も表示キャリアに合わせて変換」という処理が手軽にしかも高速にできるすぐれものです。 インストールは以下のコマンドで完了します。
$ pear channel-discover openpear.org
$ pear install openpear/PictgramConverter-alpha
詳しく知りたい方は以下の勉強会資料もごらんください。 アシアルKLab合同勉強会で発表しました

エコーサーバー改良版

takada-atです。 前回HaskellおよびRubyでエコーサーバーを発表したところ、エコーサーバーおよびネットワークプログラミングの基礎について、社内でいろいろな指摘を受けました。 今回は、指摘された点をひとつひとつ改良していきたいと思います。 リンク: Haskellでエコーサーバー

ポート番号

実は恥しながらRFCにエコーサーバーの規定があるのを知らなかったのですが、一般に「エコーサーバー」と言った場合、正式には「RFC862 - Echo Protocol」のサーバー実装を指すことが多いようです。 http://www.faqs.org/rfcs/rfc862.html RFC862では、エコープロトコルのポート番号に 7 を割り当てています。
A server listens for TCP connections on TCP port 7.
もちろん1024以下のポート番号を利用するには、ルート権限が必要ですが、可能ならポート番号7を利用することが望ましいでしょう。

forkについて

forkしたあと、親プロセスでハンドラを閉じていないという指摘を受けましたが、これは誤解で、GHCの「forkIO」「forkOS」は、forkシステムコールとは無関係な、スレッドを新たに生成する関数です。名前が少しまぎらわしいですね。 なお余談ですが「forkIO」は疑似スレッド、「forkOS」はネイティブスレッドを生成します。 さらに余談ですが、http://ja.wikipedia.org/wiki/食事する哲学者の問題って、ひょっとして「フォーク」と「fork」をかけてるんですかね? 大発見だと思ったんですが、全然関係ない上に間違ってますか、そうですか……。 参考リンク: Control.Concurrent

シグナルハンドリングについて

いくつかのシグナルをハンドリングしておかなければ、クライアントの動作によってサーバー自体がダウンしてしまいます。 特に危険なのがSIGPIPEです。 ソケットに対し、書き込みを行なった場合、相手側がすでにclose状態だとこのシグナルが発生します。デフォルトの動作ではプロセスが強制終了してしまいます。 参考リンク: シグナル (ソフトウェア) - Wikipedia SIGPIPE - Wikipedia, the free encyclopedia 以上をふまえた上で、高レベルAPIを提供するNetworkライブラリではなく、より低レベルなNetwork.Socketライブラリを使うように書き換えてみます。 main部分は以下のように変わりました。

main = withSocketsDo $ do
         let port = fromIntegral 7
         soc <- socket AF_INET Stream 0
         addr <- inet_addr "0.0.0.0"
         let sockaddr = SockAddrInet port addr
         bindSocket soc sockaddr
         listen soc 5
         -- mainスレッドではいくつかのシグナルをブロック
         blockSignals $ list2set [sigPIPE]
         putStrLn $ "start server, listening on: " ++ show port
         acceptLoop soc `finally` sClose soc

list2set = foldr addSignal emptySignalSet

read, writeについて

以前のバージョンではソケットからの読み込み・書き込みにhGetLine, hPutStrLnを利用していたのですが、これを使うと、サーバー・クライアント間の改行コードの違いなどによって問題が発生しうるという指摘を受けました。 エコープロトコルの実装としては、行ごとの読み込みではなく、文字列をすぐ読み込んで書き込む方が望ましいでしょう。

def echo_do(soc)
    while true
        buf = soc.recv(1)
        soc.write(buf)
    end
end

テスト

以上の動作確認をtelnetを手動で立ち上げて確認するのではなく、Rubyによるテストスクリプトを作成し、こちらで動作確認を行なうことにしました。

require 'test/unit'
require 'socket'

class EchoTest < Test::Unit::TestCase
    def test_echo
        #エコーのテスト
        soc = TCPSocket::new("localhost", 7)
        ["abc", "ab\na", "\n"].each do |s|
            soc.write(s)
            buf = soc.read(s.size)
            assert_equal(s, buf)
            puts buf
        end
        soc.close
    end
    def test_concurrent
        #同時接続のテスト
        socs = []
        3.times do |i|
            Thread::fork(i,socs){ |i, socs|
                sleep 0.1
                soc = TCPSocket::new("localhost", 7)
                s = "hoge"
                soc.write(s)
                buf = soc.read(s.size)
                assert_equal(s, buf)
                puts buf
                socs << soc
            }
        end
        (ThreadGroup::Default.list - [Thread.current]).each {|th| th.join}
        socs.each {|s| s.close}
    end
end

ソースコード

Haskell版とRuby版、修正したものを以下に掲載します。 なお、Haskell版のコンパイルはthreadedオプションを付け以下のようにやってください。
ghc -threaded --make -o echo2 echo2.hs

-- | an implementation for rfc862 Echo Protocol
-- http://www.rfc-editor.org/rfc/rfc862.txt

module Main where
import Network.Socket
import System.IO
import System.Posix.Signals --syghandling
import Control.Exception
import Control.Concurrent
import Prelude hiding (catch)



main = withSocketsDo $ do
         let port = fromIntegral 7
         soc <- socket AF_INET Stream 0
         addr <- inet_addr "0.0.0.0"
         let sockaddr = SockAddrInet port addr
         bindSocket soc sockaddr
         listen soc 5
         -- mainスレッドではいくつかのシグナルをブロック
         blockSignals $ list2set [sigPIPE]
         putStrLn $ "start server, listening on: " ++ show port
         acceptLoop soc `finally` sClose soc

list2set = foldr addSignal emptySignalSet



acceptLoop soc = do
  (nsoc, addr) <- accept soc
  forkOS $ echoLoop nsoc
  acceptLoop soc


echoLoop soc = do
  --メインスレッドで無視してたシグナルをunblock
  unblockSignals $ list2set [sigPIPE]
  sequence_ (repeat (do { -- ioアクションの無限リスト
                          (buff,_,_) <- recvFrom soc 1;
                          send soc buff
                     }))
  `catch` (\(SomeException e) -> return ())
  `finally` sClose soc

require 'socket'
def main
    soc = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
    sockaddr = Socket.sockaddr_in(7, "0.0.0.0")
    soc.bind(sockaddr)
    soc.listen(5)
    puts "start server, listening on 7"

    #sigpipeを無視
    Signal::trap(:PIPE, "SIG_IGN")

    accept_do(soc)
end

def accept_do(serv)
    while(true)
        soc, addr = serv.accept
        Thread.new(soc, &self::method(:echo_do))
    end
end
def echo_do(soc)
    while true
        buf = soc.recv(1)
        soc.write(buf)
    end
end

main if($0==__FILE__)
 KLab若手エンジニアブログのフッター