2011年 3月 の投稿一覧

Node.jsのイベントループ

Node.jsのイベントループについて

JavaScriptはシングルスレッドなので「並行処理」を行うために、「ノンブロッキングI/O」で「非同期処理」を実現しており
Node.jsはイベントループと呼ばれるモデルを採用しています。

イベントループのイベントキューにイベントが登録され、イベントハンドラを実行します。
イベントハンドラの完了した結果は、イベントキューにコールバック関数を登録することで受け取ります。

そのため、シングルスレッドでブロックするI/Oを行った場合、そのイベント完了後に次のイベントが実行されてしまう。
並行処理させるためには、ノンブロッキングI/Oを使ってイベントの完了を待たずに次のイベントハンドラを実行させます。

Node.jsはイベントを生成と処理の制御を行うので
ファイルのリード、DBへのクエリ発行、HTTPサーバがリクエストを受けた時などにイベントが生成されます。

例えばテストファイルを作って試すとわかります。

test.js

var oHttp = require('http');
var oFs = require('fs');
oHttp.createServer(function(req, res) {
    console.log("Start");
    oFs.readFile('index.txt', function(err, content) {
        res.writeHead(200, { 'Content-Type':'text/html; charset=utf-8' });
        res.end(content);
        console.log("Response");
    });
    console.log("End");
}).listen(8080, '127.0.0.1');
console.log('ListenStart');

読み込むファイル

<html/>
 <head/>
   <title/>index</title/>
 </head/>
  <body/> みれた? </body/>
</html/>

実行

[root@dti-vps-srv34 ~]#node test.js

ブラウザで「http://127.0.0.1:8080」にアクセス。

[root@dti-vps-srv34 ~]#node test.js
ListenStart
Start
End
Response

EndがResponseより先になっています。
これはreadFile()にコールバック関数を登録しているので、非同期となり
読み込み完了した時点で関数が呼び出されているからです。

プログラムの書き方によっては非同期の部分をロックしてしまうので注意。

Node.jsとは

Node.jsとはサーバサイドJavaScriptの一種

サーバサイドJavaScriptとはサーバサイドのアプリケーションの実装言語として、JavaScriptを使用する言語および環境のこと
ブラウザ(クライアント)サイドと同じ開発言語で、サーバサイドの開発が出来るので、開発効率が上がるといわれている。

サーバサイドJavaScriptにはこれらがある。

  • Node.js
    googleが開発したエンジン、V8で実行する環境
  • Rhino
    JavaVM上で実行するJavaScript言語処理系
  • Aptana Jaxer
    Aptana社がオープンソースとして配布している。

でもそれだけならそんなに自分は興味を持ちませんでした。
node.jsの何がすごいと感じたかというと

  • WebSocketが簡単そう。
  • サーバーの負荷分散に期待あり。

です。

WebSocketが簡単そうは「node.jsでTwitteのタイムライン取得」でも書きましたが
ほんとに簡単に実装が出来ました。
npmというパッケージマネージャーでフレームワークなどのライブラリを取り込めるのでいろいろ可能性があります。

負荷分散はC10k 問題で「apache vs nginx」=「スレッド対イベントループ」を取り上げています。
Apacheはスレッド、nginx はイベントループで、スレッドモデルは実行スタックをコピーするので
スレッド(アクセス)が増えるほどメモリ使用率が上昇するが、イベントループはプロセスは1個なので
メモリ使用量はあまり上がらないので、大量のアクセスに関してはイベントループが有利ということ。

そして、Node.jsは後者のイベントループ
これによって、特定の重い処理を待つことなく、次の入力を受け取れるので
トランザクションを重視するより、短縮URLのようなIOを優先されるような場合で活用できそう。

MySQLのパスワード忘れ

本番稼動しているサーバならともかく、テストで色々やっていると
MySQLのrootのパスワードを忘れてしまったことがあります。

対策はこれをつけて、MySQLの再起動を行うと、パスワード認証なしでアクセスできます。

vi /etc/my.conf
追加→skip-grant-tables

mysql_safeの起動オプションでも指定できます。
その場合は「–skip-grant-tables」で実行します。

起動後、MySQLへログインして、パスワードのリセットを行いましょう。

SET PASSWORD FOR ユーザ@"ホスト名"=password('パスワード');

リセット後は「skip-grant-tables」は危ないので、必ず消してください。

MySQLのバイナリログへの書き込み

バックアップとしてバイナリログのことを書いたが
動きのあるサイトのバイナリログを見てたら、どうやら書き込みが遅い?というかリアルタイムではない。

環境にもよるのかわかりませんが、ある程度溜まってから書き込まれます。

答えはmy.confへの設定

sync_binlog=1

これでコミットがリアルタイムに反映されるようになった。

いろいろな資料を見ていると
InnoDBの場合はデータファイルへの書き込みやInnoDBログファイルの方が
IOは多いらしいので、書き込みドライブを変える等すれば性能低下はすくなく、対障害性をあげられそう。

MySQLのバックアップ

サーバ構築するたびに思い出しがてら調べるのが面倒なので、メモ

一番簡単?なコールドバックアップから。
インスタンスを止めた上体から全バックアップする。

MySQLのdatadirは「/data/mysql」の仮定ですすめます。

まずは、停止

①/etc/rc.d/init.d/mysqld stop
②mysqladmin shutdown --user=root --password=pass123 --soket=/data/mysql/mysql.sock
③kill 'cat /data/mysql/mysql.sock'

止まったか確認

ps -ef | grep mysqld grep -v grep

ソケットの場所などは環境に合わせてください。

止まっていれば

cp -rp /data/mysql /home/backup/mysql-backup

この場合は、ドライブの異なるhomeへとりました。
外部デバイスなどに逃がすか、とった後別のサーバへ転送したほうがいいです。

これで一式を退避させることができるので、戻す場合は

rm -rf /data/mysql
cp -rp /home/backup/mysql-backup /data/mysql 

でもこの方法だと、止めなきゃいけない+最新には戻せない(かも)なので
タイミングにもよるけど、運用には耐えられない。
なので、1週間に1回日曜にフルでとって、それ以外トランザクションログを使ってできる限り最新状態に近づける。

トランザクションファイルはmy.confに設定を追加する。
サーバ構築するたびに思い出しがてら調べるのが面倒なので、メモ

一番簡単?なコールドバックアップから。
インスタンスを止めた上体から全バックアップする。

mysqlのdatadirは「/data/mysql」の仮定ですすめます。

まずは、停止

①/etc/rc.d/init.d/mysqld stop
②mysqladmin shutdown --user=root --password=pass123 --soket=/data/mysql/mysql.sock
③kill 'cat /data/mysql/mysql.sock'

止まったか確認

ps -ef | grep mysqld grep -v grep

ソケットの場所などは環境に合わせてください。

mkdir /data/mysql/bin_log
chown mysql:mysql /data/mysql/bin_log
vi my.conf
追加 → log-bin=/data/mysql/bin_log/mysqllog

設定が終わったら、mysqlを再起動。
これで、/data/mysql/bin_log/に「mysqllog.000001」というログができます。

この中にはトランザクションの情報が記載されています。
バイナリ形式なので、見たい場合は

mysqlbinlog --disable-log-bin /data/mysql/bin_log/mysqllog.000001 > /tmp/mysqllog.000001.sql 

でSQL形式に変換できます。

ログはどんどん書き込まれるので、スイッチする場合は

mysqladmin flush-log --user=root --password=pass123 --soket=/data/mysql/mysql.sock

これで、連番の別ファイルが作成されてそっちに書き込まれるようになります。

2つのバイナリを反映させる場合は

mysqlbinlog --disable-log-bin /data/mysql/bin_log/mysqllog.000001 /data/mysql/bin_log/mysqllog.000002 > /tmp/mysqllog.sql 
mysql --user=root --password=pass123 --soket=/data/mysql/mysql.sock < /tmp/mysqllog.sql 

ってな感じです。

XperiaがAndroid2.3へアップデート予定

SonyEricssonが Xperia X10のAndroid2.3(Gingerbread)へのアップグレードを公表しました。

去年の今頃に購入し、1.6から2.1へアップデートされ、シングルタッチからマルチタッチ対応など、今までの携帯ではありえないほどの
アップデートがされてきたXperiaですが、さらに2.3へのアップグレード予定が発表されました。
情報はSony Ericsson Product Blogで公開されています。

ソース

エキサイトでの翻訳抜粋

 私たちは、今年の終わりのQ2/前のQ3のときに一般的な貿易キットへの
XperiaTM X10にアップグレードをAndroid Gingerbreadにもたらすのを計画しています。 
私たちは、ずっとあなたの要求を聞いていますが、今日までこれを確認できませんでした。
 2011年の私たちの製品のXperiaTM PLAY、XperiaTMアーク、
およびXperiaTMへのかなりの作業は、新実際に特別プロジェクトにおける仕事が
GingerbreadをXperiaTM X10に私たちがもたらし始めるのを可能にしました。

XDAなどではカスタム2.3が導入されていたので
無理は無いだろうと思っていましたがまさかの公式アップデートです。

メモリ以外は、めだって?大きく進化していないArcにどこまで追いつけるか見ものです。

Androidマーケットの情報をPHPでとってくる。

Androidマーケットの情報をPHPで取得

今は公式にPC版マーケットが出てきましたが、今まではAndroidマーケットアプリからしかアクセスできず不便でした。
この記事そんなときに見つけた、PHPからマーケットアプリの通信をマネてアクセスする方法です。

Android Market API for PHPの使い方

→http://code.google.com/p/android-market-api-php/
使い方は簡単でまずは「local.php」にGoogleアカウントを設定する。
sampleがあるので、実行してみるとちゃんと取れる。

ざっとみたかんじ大体こんな感じ?

MarketSession.php:キャリアやバージョンなどの設定はここにある。
market.proto.php:どんな検索できるかな?はここをみる。

DOCOMOにしたかったので、MarketSession.phpを下記のように変更

[code language=”php”]
function __construct () {
$this->context = new RequestContext();
$this->context->setUnknown1(0);
$this->context->setVersion(1002012);
$this->context->setDeviceAndSdkVersion("passion:8");
$this->context->setUserLanguage("ja");
$this->context->setUserCountry("JP");
$this->setOperatorDocomo();
}
public function setOperatorDocomo() {
$this->setOperator("NTT DOCOMO", "44010");
}
[/code]

サンプル試していて、プロモーションテキストなど取れないかなって見てたら、newした後にsetWithExtendedInfoをtrueにしてあげる必要がある。

[code language=”php”]
$oAppReq = new AppsRequest();
$oAppReq -> setWithExtendedInfo(true);
[/code]

とりあえず、それっぽく検索はできる様になったけど、日本語検索がマーケットアプリとは違う動きをするときがあるので調査中。

サンプルページはコチラ

node.jsとnpmのトラブル

node.jsのパッケージマネージャーである、npmを使ってるが
いろんなマシンで試したけど、何かとトラブルがでる。

いろいろって言いながら、全部CentOSです。

・VMwere上のCentOS5.5
・VirtualBox上のCentOS5.5
・物理マシン上のCentOS5.4
・ServerMan上のCentOS5.5

仮想ソフトは関係ないと思うけど、いろいろトラブルからインストール時に
~develとかのパッケージを入れたり、試してみた。
今のところ一番うまく言ったのはVirtualBoxの仮想マシン。
本当はServerManでうまくいってほしかった。

絶対起こるのはhttp-basic-authのインストールでJSONのパースエラー
JSON書き直してローカルインストールすればOKそうだけど、とりあえず、http-basic-auth.jsを直でおいて呼び出して回避

[root@dti-vps-srv34 ~]# npm install http-basic-auth
npm info it worked if it ends with ok
npm info using npm@0.3.15
npm info using node@v0.4.2
npm info fetch http://registry.npmjs.org/http-basic-auth/-/http-basic-auth-0.1.0.tgz
npm ERR! couldn't read package.json in /tmp/npm-1300626776833/1300626776833-0.38027541153132915/contents/package
npm ERR! Error installing http-basic-auth@0.1.0
npm ERR! Error: Failed to parse json
npm ERR! Unexpected token ILLEGAL
npm ERR!     at jsonParseFail (/usr/local/lib/node/.npm/npm/0.3.15/package/lib/utils/read-json.js:89:11)
npm ERR!     at /usr/local/lib/node/.npm/npm/0.3.15/package/lib/utils/read-json.js:82:14
npm ERR!     at P (/usr/local/lib/node/.npm/npm/0.3.15/package/lib/utils/read-json.js:62:40)
npm ERR!     at cb (/usr/local/lib/node/.npm/npm/0.3.15/package/lib/utils/graceful-fs.js:31:9)
npm ERR!     at [object Object]. (fs.js:86:5)
npm ERR!     at [object Object].emit (events.js:39:17)
npm ERR!     at afterRead (fs.js:843:12)
npm ERR! JSON.parse 
npm ERR! JSON.parse Failed to parse package.json data.
npm ERR! JSON.parse Note that package.json must be actual JSON, not
npm ERR! JSON.parse just a JavaScript object.
npm ERR! JSON.parse 
npm ERR! JSON.parse This changed in npm 0.3.0, and is not a bug in npm.
npm ERR! JSON.parse Tell the package author to fix their package.json file.
npm ERR! JSON.parse
npm ERR! System Linux 2.6.18-194.3.1.el5.028stab069.6
npm ERR! argv { remain: [ 'http-basic-auth' ],
npm ERR! argv   cooked: [ 'install', 'http-basic-auth' ],
npm ERR! argv   original: [ 'install', 'http-basic-auth' ] }
npm not ok
[root@dti-vps-srv34 ~]# 

次にServerManで初めてでたbase64が無いよエラー
今までbase64をあえてインストールした記憶無いのに。

[root@dti-vps-srv34 node]# node StreamTwitter.js

node.js:116
        throw e; // process.nextTick error, or 'error' event on first tick
        ^
Error: Cannot find module 'base64'
    at Function._resolveFilename (module.js:299:11)
    at Function._load (module.js:245:25)
    at require (module.js:327:19)
    at Object. (/var/www/node/http-basic-auth.js:3:14)
    at Module._compile (module.js:383:26)
    at Object..js (module.js:389:10)
    at Module.load (module.js:315:31)
    at Function._load (module.js:276:12)
    at require (module.js:327:19)
    at Object. (/var/www/node/StreamTwitter.js:16:12)
[root@dti-vps-srv34 node]# 

インストールしたけど、なんかエラーがでてできなかった。
「cxx: base64.cc -> build/default/base64_1.o」
base64はID,PASSのencodeだけだから適当なツールで変換してソースにべた書きしました。

なんで失敗するのかと思って、npmのフォルダに無理やり上手くいったOSのファイル持ってきたり
いろいろしたけど、当然だめでした。
でも、ある20日に再チャレンジしたらなんか上手くいった。

[root@dti-vps-srv34 node]# npm install base64
npm info it worked if it ends with ok
npm info using npm@0.3.15
npm info using node@v0.4.2
npm info fetch http://registry.npmjs.org/base64/-/base64-2.0.3.tgz
npm info calculating sha1 /usr/local/lib/node/.npm/.cache/base64/2.0.3/package.tgz
npm info shasum d58b08f621b4be0c6b768258821d4d5f402c35b8
npm info preinstall base64@2.0.3
npm info install base64@2.0.3
Checking for program g++ or c++          : /usr/bin/g++ 
Checking for program cpp                 : /usr/bin/cpp 
Checking for program ar                  : /usr/bin/ar 
Checking for program ranlib              : /usr/bin/ranlib 
Checking for g++                         : ok  
Checking for node path                   : not found 
Checking for node prefix                 : ok /usr/local 
'configure' finished successfully (0.391s)
Waf: Entering directory `/usr/local/lib/node/.npm/base64/2.0.3/package/build'
[1/2] cxx: base64.cc -> build/default/base64_1.o
[2/2] cxx_link: build/default/base64_1.o -> build/default/base64.node
Waf: Leaving directory `/usr/local/lib/node/.npm/base64/2.0.3/package/build'
'build' finished successfully (1.856s)
npm info postinstall base64@2.0.3
npm info preactivate base64@2.0.3
npm info activate base64@2.0.3
npm info postactivate base64@2.0.3
npm info build Success: base64@2.0.3
npm ok
[root@dti-vps-srv34 node]# 

最後にこれも調べ途中だけど、node.jsのWebsocketって何種類かあるっぽい

これをつかったけど、VMwere上のCentOS5.5はエラーは出ないけど
websocketのコネクションははれなかった。

[root@dti-vps-srv34 node]#npm install websocket-server

他の環境はこっちを試したら上手くいてる。

[root@dti-vps-srv34 node]#npm install http://github.com/miksago/node-websocket-server/tarball/master 

まだまだ、日本語の情報も少ない。

CentOS5.5でPHPの最新版(5.3)をインストール

脆弱性対策としてPHPの5.3をインストールする必要が出た。

CenstOSだから、最新はきついと思いながら調べたらレポジトリ追加でなんとかなった。

remiというレポジトリをどうやら追加しておこなう。
ココではないけど、MySQLの使ってるので、MySQLもいっしょにアップデートの必要があるらしい。

テストで違うサーバでやったら一回失敗して、mysqlが壊れて再インストールしましたw
大丈夫だと思うけど、mysqldumpでバックアップはしておきましょう。

yumに「epel」と「remi」を追加する。

#rpm -ivh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-4.noarch.rpm
#rpm -ivh http://rpms.famillecollet.com/enterprise/remi-release-5.rpm

常用しないように「epel」と「remi」の「enabled=0」を設定(なっているはず)

#vi /etc/yum.repos.d/epel.repo
#vi /etc/yum.repos.d/remi.repo

yumのオプションで「epel」と「remi」を有効にしてアップデート
phpだけだど、mysqlがらみでエラーがでるので、いっしょに。

#yum update --enablerepo=remi,epel php mysql mysql-server

完了。

# php -v
PHP 5.3.0 (cli) (built: Jul 19 2009 17:55:08)
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2009 Zend Technologies

マーケットのデベロッパー登録

Androidマーケットに開発者登録してみた。

http://market.android.com/publish/Home

住所とかは国を選べばそれっぽく入れられるけど
電話番号入力ってあって、家電がない。。

でも電話番号は携帯でもOKだった。
注意点として頭に+81をつけて、先頭の0を消す。

番号が080-9876-5432なら、+818098765432