2013年4月4日木曜日

Mountain LionでPHPが動く環境を作る

まずはインストールからかと思いきや、ApahceもPHPもインストールされていた。
mac:~ tadasuke$ sudo apachectl start
とやると、とりあえず起動するっぽいので、ブラウザから
http://localhost
とアクセスしてやると、
It works!
が表示された。

ただ、ドキュメントルートが
/Library/WebServer/Documents
となっていてイケていないので、設定を変更してやる。

設定ファイルは
/etc/apache2/httpd.conf
にあったので、
#DocumentRoot "/Library/WebServer/Documents"
DocumentRoot "/web/tadasuke/http"
こんな感じに変更。

で、
Macintosh:~ tadasuke$ sudo apachectl restart
で再起動して改めてアクセスするとエラー発生。

どうやら、
/Library/WebServer/Documents
以外のディレクトリはアクセスができない設定になっている模様。

細かい対策は色々あるのだけど、とりあえず面倒なので
<Directory />
    Options FollowSymLinks
    AllowOverride None
    Order deny,allow
#    Deny from all
    Allow from all
</Directory>
としてやって、アクセスを許可してみたところ、意図したとおり表示された。

ふぅ。

PHPについてはもっと簡単で、同じくhttpd.confで
#LoadModule php5_module libexec/apache2/libphp5.so
となっているので、ここのコメントアウトをとってやればよいだけ。

思いの外簡単にできました。。。

2013年3月21日木曜日

git pushでのミスを防ぐ

通常は
git push origin hoge
という、もはや何百回タイプしたか分からないやり方でプッシュをしている。

通常であれば問題ないのだけど、何かトラブルなどがあった場合、間違えて
git push origin master
などとしてしまい、余計に被害を拡大してしまったことが何度かあった。

これをどうにかして防ぎたいなーと思って色々調べてみたところ、引数なしのgit pushは危険なので気をつけましょうというとても参考になるページを発見した。

Gitの設定で、
git config --global push.default current
とすると、引数に何も設定せずに
git push
としただけで、カレントブランチのみをpushしてくれる。

本当は
git config --global push.default simple
とすると、カレントブランチと同名のブランチがリモートにあった場合のみpushしてくれるのでバッチリなのだけど、バージョンが1.7.11以降でないと使えないらしく、自分の環境の1.7.4だったので断念。

でも、とりあえずこれで今までよりは安全にpushができるようになったかな。

2013年2月3日日曜日

脳内メモリとコーディング

ソーシャルゲームの開発現場に入ってから、過去にない量のコードを書いている。

過去に働いていたウォーターフォール型の現場では2,3週間コードを1行も書かない日が続くなんてことはザラにあったのだが、ここ数年は3日連続でコードを書かかなかった記憶はほぼない。

夏休み、正月休みなどでも結局何かしらのコードは書いているし・・・
別に愚痴を言っているわけではなく「コードを書いている時間=至福の時間」の自分にとっては、これ以上ない幸せな現場で働かせて頂いている。

さて、そのようにほぼ毎日コードを書いていると、色々と気付くことがある。

下記はあくまでもワタクシ個人の場合であって、プログラマーを代表しての発言ではないことを先にお伝えしておきます。

コードを書く際に重要なのは、脳内メモリだ。

これから書くコードの内容、重要なポイント、気をつけなければならない点などを過去の記憶やPC上のドキュメントなどから呼び出して脳内メモリに展開し、それからコーディングを開始する必要がある。

この作業が実はとても重要で、良い感じにメモリに展開できればかなりのハイスピードでコードを書き続けることができるのだが、上手く展開できないときは、いちいち脳内ハードディスクの『記憶』領域やPC内、ネット上のドキュメントにアクセスしなければならないので、著しくスピードが落ちる。

逆にこのメモリ展開が上手く言った時などは、自分のようなヘボ脳内CPUの持ち主でもタイピングが追いつかないレベルでコードが浮かんでくる。

なので、この『脳内メモリ展開』さえ上手くいってくれれば常にハイパフォーマンスを発揮できるわけだが、これがなかなか難しい。

脳内メモリはメモリなので、容量が少なく、上書きされやすい。
ようするに、外部からの割り込み処理に極めて弱いのだ。

このあたりが自分が自宅作業が苦手な理由でもあるのだけど、せっかく色々な情報を脳内メモリに展開でき、「さーこれからコーディング!!」となったあたりで話しかけらるなどの外部からの割り込み処理があると、せっかくの脳内メモリが上書きされてしまう。

で、また新たに展開できたころにさらに割り込み処理が起こる。

なんてことが繰り返されると、さすがに心が折れてくる。

また、もう一つ、この脳内メモリ型コーディングの問題点としては、自分が書いたコードを忘れやすいということがある。

「実装したと思っていた機能を実装していなかった」
というのならまだ分かるのだが、それなりにやっかいな処理をバッチリ書いたのに、そのことがさっぱり記憶になく、後になって「あれっ?できてるじゃん!!」と驚いたことは過去に何度もある。

というわけで、色々と問題点があるコーディング技法?なのだけど、自分としてはこれ以外のやり方を持っていないので、引き続き頑張りますよー。

ただ、さすがにコードを書いたことをすっかり忘れているというのは色々な意味で危険ではあるので、何かしら対策を考えます・・・

2013年1月31日木曜日

Zend_QueueでActiveMQを使う_その2

さて、その1の続き。

ひたすらZendのソースを追ってみたところ、その1で作ったMy_Queue_Stomp_Clientクラスを使うようにするにはZend_Queueのインスを作成する際のパラメータで、オブジェクトを渡してやれば良いことが判明。
$stompClient = new My_Queue_Stomp_Client( Zend_Queue_Adapter_Activemq::DEFAULT_SCHEME );
$options = array(
    'name'          => $name
  , 'driverOptions' => array( 'stompClient' => $stompClient )
);
$activeMq = new Zend_Queue( 'Activemq', $options );
後は今までどおり
$msgQueue = $activeMq -> receive( 1 );
とやれば、キューを一つ取り出すことができた。

とりあえずこれで最低限使える状態にはなったのだけど、このやり方だとキューを1つしか取り出すことができない。
マニュアルを読んだところ
$msgQueue = $activeMq -> receive( 5 );
のようにすると、キューを5つ取り出すことができるらしい。
というわけで早速試してみたところ、またも微妙に意図した動きをしてくれない。
キューが5件以上ある時は問題なく5件取得できるし、キューが1件もない時はすぐに終了してくれるのだけど、キューが5件未満だった場合、いつまで待ってもレスポンスが返ってこない。
またもZendのソースを追ってみたところ、今度はかなり難解だった。
というわけで今度は素直に断念して、以下のようなコードで対応することに。
$insertDataCount = 0;
$queueArray = array();
while ( 1 ) {
  
  // ActiveMQから一件ずつ取得
  $msgQueue = $activeMq -> receive( 1 );
  $queue    = $msgQueue -> current();

  // 取得できなければ終了
  if ( is_null( $queue ) === TRUE ) {
    break;
  } else {
    ;
  }
    
  // Bodyを抽出
  $message = $queue -> body;
    
  // 内部変数にプール
  $queueArray[] = $message;
    
  // キューを削除
  $activeMq -> deleteMessage( $queue );
    
  // 制限数を超えたら終了
  $insertDataCount++;
  if ( $insertDataCount >= 10000 ) {
    break;
  } else {
    ;
  }
    
}


久々にちゃんとZendのソースを読むのはかなり疲れる作業だったけど、色々と仕組みがわかってとても勉強になりましたとさ。

2013年1月30日水曜日

Zend_QueueでActiveMQを使う_その1

DBサーバの負荷を軽減するため、キューサーバを利用し、非同期処理をすることになった。
そこで、Zend_Queueを使い、ActiveMQに対してキューの出し入れをする。

キューを送信するのはマニュアルに従って、
$activeMq = new Zend_Queue( 'Activemq', array( 'name' => '/queue/hoge' ) );
$activeMq -> send( $message );
のような感じに書いたら、とりあえず問題なく動いてくれた。

こいつは予想外に簡単♪と思い今度は受信するために
$activeMq = new Zend_Queue( 'Activemq', array( 'name' => '/queue/hoge' ) );
$msgQueue = $activeMq -> receive( 1 );
とやってみたところ、どうにも挙動がおかしい。
ActiveMQにキューが1件以上ある場合はすぐにレスポンスが返ってくるのだけど、キューが1件もないと5秒程度レスポンスが返ってこない。

Zend_Queueのコンストラクタのパラメータに"timeout"というものが設定できるのだが、その値を変更しても挙動は何も変わらない。
というわけで、Zendの中身を見てみることにした。

Zend_QueueでAcitveMQと接続する流れとしては、
$msgQueue = $activeMq -> receive( 1 );
    public function receive($maxMessages=null, $timeout=null)
    {
        if (($maxMessages !== null) && !is_integer($maxMessages)) {
            require_once 'Zend/Queue/Exception.php';
            throw new Zend_Queue_Exception('$maxMessages must be an integer or null');
        }

        if (($timeout !== null) && !is_integer($timeout)) {
            require_once 'Zend/Queue/Exception.php';
            throw new Zend_Queue_Exception('$timeout must be an integer or null');
        }

        // Default to returning only one message
        if ($maxMessages === null) {
            $maxMessages = 1;
        }

        // Default to standard timeout
        if ($timeout === null) {
            $timeout = $this->getOption(self::TIMEOUT);
        }

        return $this->getAdapter()->receive($maxMessages, $timeout);
    public function receive($maxMessages=null, $timeout=null, Zend_Queue $queue=null)
    {
        if ($maxMessages === null) {
            $maxMessages = 1;
        }
        if ($timeout === null) {
            $timeout = self::RECEIVE_TIMEOUT_DEFAULT;
        }
        if ($queue === null) {
            $queue = $this->_queue;
        }

        // read
        $data = array();

        // signal that we are reading
        if (!$this->_isSubscribed($queue)){
            $this->_subscribe($queue);
        }

        if ($maxMessages > 0) {
            if ($this->_client->canRead()) {
    public function canRead()
    {
        return $this->getConnection()->canRead();
 public function canRead()
    {
        $read   = array($this->_socket);
        $write  = null;
        $except = null;

        return stream_select(
            $read,
            $write,
            $except,
            $this->_options['timeout_sec'],
            $this->_options['timeout_usec']
        ) == 1;
        // see http://us.php.net/manual/en/function.stream-select.php
    }
と、ここまで追って、やっとPHPの標準メソッドに到着。
ふぅ。

調べてみると、このstream_selectというメソッドの第四、第五引数で接続の待ち時間を設定するらしい。
というわけで、この値の初期値を調べてみると、
    const READ_TIMEOUT_DEFAULT_USEC = 0; // 0 microseconds
    const READ_TIMEOUT_DEFAULT_SEC = 5; // 5 seconds
となっていて、これが原因で5秒間レスポンスが返ってこないことが判明。
というわけで、後はこの値を変えてやればいいのだけど、これが絶妙に難しい。
Connection.php側では、
    public function open($scheme, $host, $port, array $options = array())
    {
        $str = $scheme . '://' . $host;
        $this->_socket = fsockopen($str, $port, $errno, $errstr);

        if ($this->_socket === false) {
            // aparently there is some reason that fsockopen will return false
            // but it normally throws an error.
            require_once 'Zend/Queue/Exception.php';
            throw new Zend_Queue_Exception("Unable to connect to $str; error = $errstr ( errno = $errno )");
        }

        stream_set_blocking($this->_socket, 0); // non blocking

        if (!isset($options['timeout_sec'])) {
            $options['timeout_sec'] = self::READ_TIMEOUT_DEFAULT_SEC;
        }
        if (! isset($options['timeout_usec'])) {
            $options['timeout_usec'] = self::READ_TIMEOUT_DEFAULT_USEC;
        }

        $this->_options = $options;

        return true;
    }
となっているので、このメソッドの第四引数で設定できるようになっているのだけど、呼び出し元が
    public function addConnection($scheme, $host, $port, $class = 'Zend_Queue_Stomp_Client_Connection')
    {
        if (!class_exists($class)) {
            require_once 'Zend/Loader.php';
            Zend_Loader::loadClass($class);
        }

        $connection = new $class();

        if ($connection->open($scheme, $host, $port)) {

となっていて、そもそも第四引数が存在しない。
そして、Connection.php側では上記メソッド以外で該当の変数を変更する手段が用意されていない・・・

対応方法としては
  • Connection.phpのREAD_TIMEOUT_DEFAULT_SECの値を書き換える。
  • 継承したクラスを作って頑張る
の2つ。
簡単なのは前者なのだけど、Zendのソースを書き換えて使うというのはかなり抵抗があるので、後者にチャレンジすることに。

継承したクラスを作るのは簡単で、
require_once 'Zend/Queue/Stomp/Client.php';

class My_Queue_Stomp_Client extends Zend_Queue_Stomp_Client {
 
 /**
  * (non-PHPdoc)
  * @see Zend_Queue_Stomp_Client::addConnection()
  */
 public function addConnection($scheme, $host, $port, $class = 'Zend_Queue_Stomp_Client_Connection') {
  
  OutputLog::outLog( OutputLog::INFO, __METHOD__, __LINE__, 'START' );
  
        if (!class_exists($class)) {
            require_once 'Zend/Loader.php';
            Zend_Loader::loadClass($class);
        }

        $connection = new $class();

        // とりあえず0.2秒レスポンスを待つことに
        if ($connection->open($scheme, $host, $port, array( 'timeout_sec' => 0, 'timeout_usec' => 200000 ) ) ) {
            $this->setConnection($connection);
            return true;
        }

        $connection->close();
        return false;
    }
}
のようにしてやればOK。
問題はどうやってこいつを使うようにするか。

かなり長くなってきたので、続きはまた今度。