node.jsの仕組み(ノンブロッキングI/Oとかスレッドとか)

サーバーサイドjavascriptで一世を風靡しているnode.jsですが、PHPで書くサーバーサイドのプログラムと何が違って何がいいのか。

「なんかいい」とかじゃ使えないしってことで調べてたら知らないことがたくさん出てきたんでメモ。

 

簡単に言うと、node.jsが今までと違うのは

「イベントループモデルで、ノンブロッキングI/Oを使用している」

点らしい。

はてな。

 

イベントループモデルとスレッドモデル

スレッドモデルとイベントループモデルは、ざっくりいうと「スレッドでの処理の仕方」を大まかに2つに分けたもの。

そもそもスレッドというものは「処理の流れ」を意味している。

 

後の説明もあるので若干分かりにくいたとえだけど、「店頭でゲームソフトを購入する」処理に例える。

分かりやすく処理の流れを店員の視点で

1.レジに空箱が持ちこまれる

2.棚まで本体を取りに行き、本体を棚から探す

3.本体を客まで持っていって会計をする

の3つ程度に分ける。

 

スレッドというのはこの一連の処理のことをいう。

一般的なWeb上のやり取りで言えば、

1.クライアント側(ブラウザなど)からフォームを送信する

2.サーバー側でデータをデータベースに挿入したり、必要なデータを取り出す

3.取り出したデータを計算したり、加工するなどして表示する

でいいと思う。

さて、そこに2人目の客が商品を持ち込んだ。

この2つのモデルの違いは、ここで店員1人が客2人分の処理を行うのがイベントループモデル

もう1人店員が出てきて会計をするのがスレッドモデルである。

 

スレッドモデルとは複数のアクセスがあったときに、スレッドを複製して処理を行うことを意味する。

スレッドが複製されればされた分、CPUを食う。

店員の例でいうとアクセスが増えるほど店員が増えていき、増やせば増やすだけ人件費(処理時の負荷)がかかる。

 

一方のイベントループモデルはアクセスが複数あっても、スレッドの数は増えない。

CPUの面でいうとスレッドモデルよりも、有利である。

店員の例でいえば、1人の店員ですべてを処理して人件費を削減しているイメージ。

 

 

しかし、2.の処理であるゲームソフト本体を棚から探し出してくる作業が実は厄介になってくる。

これがI/O(Input/Output)で、主にデータベースやネットワークなどからデータを出し入れしたりする作業を指す。

http://d.hatena.ne.jp/badatmath/20101020/1287587240

この記事によれば、これらI/Oに対する時間の掛かり方が、他の処理に比べるととんでもないらしい。

例えを用いるなら

I/O処理=希望のゲームソフトを棚から探して取り出す作業

他の処理(主にCPU上の処理)=会計など

 

この出し入れの作業にかかる時間が他の処理の1万倍多くかかるとしたら、1人の店員が全ての仕事をするスタイル(イベントループモデル)ではとても営業できない。これまであったイベントループモデルの技術は極端だがこういうイメージ。ちょっと扱いが難しい。

そういう訳でスレッドモデルが採用されているApachePHPやらなんやら)が主流でなじみ深いものになってたのかな。

 

ブロッキングI/OとノンブロッキングI/O

出し入れ作業(I/O)は時間がかかる。

このとても時間のかかるI/Oの処理で、商品が見つかるまでじっと棚の前で待っているのがブロッキングI/Oと呼ばれるモデルである。

処理が文字通りブロックされているイメージでいい。

んで、商品が見つかるまで棚の前で待たずに次のお客さんの対応に回ることができる。これがノンブロッキングI/O。他の処理を行いながら、商品が見つかり次第棚から持っていき会計する処理に取り掛かる。

出し入れ処理がブロックされない。

 

node.jsはイベントループモデルかつノンブロッキングI/Oと最初に言った。

 

node.js→レジと棚の間を1人の店員が立ち止まることなく、1~3の処理を効率よく行う。

Apache→たくさん店員が出てくるが、棚の前で商品が見つかるまでじっと待つ。

 

 

こんな感じの理解でいいですかね。

何かあればご指摘ください。

 

ソケットとかHTTPのメモ

クライアントとサーバーのコネクションの確立についていろいろ調べたのでそのまとめメモ。

 

 

Sockets

ネットを使ったコネクションはソケットというものを使って行われる。

ソケットというのは2本のパイプがクライアントとサーバーとの間にあって、その2本のパイプを使って情報のやり取りを行うもの。と考えればいい。

情報のやり取りを行う手順は

 

1.クライアント側で行先(普通はサーバーのIP)と着地点(ポート)を決めて接続

2.クライアント側がそこに何がしたいかっていう情報を流し込む

3.サーバー側がそれにこたえて情報を流し込む

4.クライアント側が流れてきた情報を読む

5.クライアント側がソケットを閉じる

 

ざっくりやるとこの一連の流れが基本らしい。クライアント側の処理は1,2,4,5の4つがあればいい。

だけどこれだとざっくりしすぎていて、

「サーバーが見つからない」

「情報を流し込んだけどサーバーが応答しない」

などなどエラー処理を自分で実装しなければならない。

そこでHTTP

 

HTTP

Webアプリケーションをつくるエンジニアとしては、そういったlower levelな処理よりその接続を使って何をするかに注力したい。

それで利用されるようになったのがHTTPっていうプロトコル(約束ごととか決め事)。

This should appeal to our laziness.なんて書かれてるからまあ、そういうことなんだろう。自分で接続作る作業に約束事が出来るから自分で考えて実装する必要がない。楽ちん。

(自分でソケット用意して接続するのはチャットなんかを作るときにやったりするらしい)

 

よく見かける404 File Not Foundとか403 ForbiddenはHTTPがやってくれるエラー表示の有名なやつ。

接続を自前で作ると、この辺も自分でやる必要が出てくる。

HTTPはソケットのところで説明したように、クライアント対サーバーのリクエスト→レスポンスのサイクルを取る。

 

 

HTTPのこのサイクルの中でどんな情報が行き来してるかを見るなら、このchromeプラグインが見やすい。

Dev HTTP Client

上部にあるアドレス欄に何かしらを入力して、Sendボタンクリックでいわゆるリクエストを送信。

下に表示されるのがサーバーからのレスポンス。

レスポンスにはHEADER情報とBODY情報がある。(htmlのheadタグbodyタグとはべつもん)

詳しいことは解説してるサイトとか書籍がいっぱいあるからそこをみながらこれで実験してみればいい。

「HTTP リクエスト」とかでぐぐればいんじゃないかな

このプラグインはリクエストの方の中身もいろいろ変えて遊べる。便利。

 

Subversion1.6 + Apache2.2 サブドメイン設定 & AuthzSVN

BasicApacheとSubversionを使用して、SVNにhttp://~でアクセスするメモ。

サブドメインを用いて、SVNにアクセスする方法を試行錯誤してみました。

 

Subversionにサーバー外部からアクセスする方法は、ざっと調べてみるとおおきくわけて、Apacheベース(WebDAVモジュール)のものとsvnserveベースのものがあるようです。

 

#アクセス認証方法
Apache + Subversion http://~でアクセス Basic認証+ authzファイルによるアクセス制御
svnserve svn://~でアクセス 認証鍵を使ってssh+svn://~でアクセス

今回はApacheベースのものでやってみました。

なお、サーバーへのSubversionApacheのインストールは済んでいるものとします。

 

ここまでの設定は以下のURLを参考に

http://halu834.blog17.fc2.com/blog-entry-28.html

http://osima.jp/blog/log-fsv-subversion/

http://park1.wakwak.com/~ima/centos4_subversion0001.html

 

達成したい条件としては

1.http://svn.example.com/ のようにSVNアクセスにサブドメインを使用する

2.Basic認証で認証機能を付ける

3.authzファイルによるアクセス制御を付ける

4.SVNGUIでチェックアウトやコミットを行えるようにする

 

1の条件では

http://svn.example.com で/home/svn/repos内のリポジトリのリストを表示

http://svn.example.com/project1/ で/home/svn/repos/project1内のbranches,tags,trunkをリスト表示

できるようにしたい。

SVNにhttpでアクセスし、Basic認証を付ける

というわけで手順。

 

まずはサブドメイン使用 + Basic認証までを設定します

Apache設定ファイルのhttpd.confを開き、以下の設定を追加します。

httpd/conf.d/以下にsubversion.confというApacheSVNモジュール用の設定ファイルが作られている場合もあります。)

NameVirtualHost *:80
<VirtualHost *:80>
    ServerName svn.example.com
    DocumentRoot /home/dummy
    ErrorLog logs/svn_error_log
    <Location />
        Require valid-user
        AuthType Basic
        AuthName "Subversion repository"
        AuthUserFile /home/svn/repos/.htpasswd
        SVNListParentPath on
        DAV svn
        SVNParentPath /home/svn/repos
        SVNAutoversioning on
    </Location>
</VirtualHost>
<Directory "/home/dummy">
    Allow from all
    AllowOverride all
</Directory>

この設定では、ホストの振り分けをすべてVirtualHostで行い、全体のDocumentRootのアクセス権をDeny from allにしているため、ダミーのディレクトリをこのホストのDocumentRootに設定し、アクセス権をAllow from allにしています。

全体のDocumentRootのアクセス権をAllow from allにしている場合はDocumentRootの指定と<Directory>ディレクティブの記述は不要になります。

<Location>ディレクティブのAuthUserFileにはBasic認証用のパスワードを指定します。

パスワードの生成は以下のURLを参考にしてください。

http://www.atmarkit.co.jp/flinux/rensai/apache10/apache10b.html

SVNListParentPathはonにしておくと、/home/svn/repos以下のリポジトリをリスト表示することができます。

SVNParentPathはリポジトリが配置される親ディレクトリのpathを指定します。

httpd.confを変更したので、Apacheを再起動します。

service httpd restart

これでhttp://svn.example.com/にアクセスし、Basic認証を行うとリポジトリのリストを表示することができます。

また、http://svn.example.com/project1/でproject1内のbranches,tags,trunkを表示します。

※AuthzSVNによる細かなアクセス制限を設けないのであれば、以降の設定は不要です。

■AuthzSVNの設定

次に、AuthzSVNを使用しリポジトリに対してアクセス制限を指定していきます。

httpd.confもしくはhttpd/conf.d/subversion.confに

LoadModule authz_svn_module   modules/mod_authz_svn.so

があることを確認しておきましょう。なければ追記してください。

先ほどの設定にAuthzSVN設定用ファイルへのアクセスパスを追加します。

NameVirtualHost *:80
<VirtualHost *:80>
    ServerName svn.example.com
    DocumentRoot /home/dummy
    ErrorLog logs/svn_error_log
    <Location />
        AuthzSVNAccessFile /home/svn/repos/authzsvn.conf
        Require valid-user
        AuthType Basic
        AuthName "Subversion repository"
        AuthUserFile /home/svn/repos/.htpasswd
        SVNListParentPath on
        DAV svn
        SVNParentPath /home/svn/repos
        SVNAutoversioning on
    </Location>
</VirtualHost>
<Directory "/home/dummy">
    Allow from all
    AllowOverride all
</Directory>

アクセスパスに指定した場所にauthzsvn.confを作成し、内容を記述していきます。

[groups]
dev1 = Mike, Jhon
dev2 = Wataru, Satoshi

[/]
* = rw

[project1:/]
@dev1 = rw
@dev2 =
[project2:/]
@dev1 = r
@dev2 = rw

[groups]ではBasic認証で使用するユーザー名によってグループを作成することができます。

例ではdev1, dev2グループを作成しています。

[/]はリポジトリルートを意味します。今回の例でいうとプロジェクトがリスト表示される部分です。

*はすべてのユーザーを意味します。

rは読み込み、wは書き込みの許可があることを指します。

両方許可しない場合は=の後に何も記述しません。

[project1:/][project2:/]のようにプロジェクトごとにアクセス権を指定することもできます。

例では、dev1グループはproject1の読み書き、project2の読み込みを行うことができます。

authzSVNの設定は以上です。

ですが、VirtualHostを設定している場合はこれだけじゃ動きません。

■※ここからが本題

今回の例のようにVirtualHostによってLocationを指定した場合、authz.conf内の[/]で指定したリポジトリルートを*=rwのように読み書き可にしているにも関わらず、http://svn.example.com/へのアクセスで「403 Forbidden」になってしまう問題が発生します。

http://svn.example.com/project1/のようにプロジェクトを指定すると、しっかり表示されるのですが。。

どうやら<Location />の指定をすると,authz.confの[/]がリポジトリルートを認識できないようです。

解決策

NameVirtualHost *:80
<VirtualHost *:80>
    ServerName svn.example.com
    DocumentRoot /home/dummy
    ErrorLog logs/svn_error_log
    <Location />
        AuthzSVNAccessFile /home/svn/repos/authzsvn.conf←ここを削除
        Require valid-user
        AuthType Basic
        AuthName "Subversion repository"
        AuthUserFile /home/svn/repos/.htpasswd
        SVNListParentPath on
        DAV svn
        SVNParentPath /home/svn/repos
        SVNAutoversioning on
    </Location>
    #追加
    <Location /repos>
        AuthzSVNAccessFile /home/svn/repos/authzsvn.conf
    </Location>
</VirtualHost>
<Directory "/home/dummy">
    Allow from all
    AllowOverride all
</Directory>

 

アクセスパスの指定だけ<Location /repos>としたディレクティブに入れるとなぜか動いてくれる・・・

なぜだかわかりませんが、これで一応解決です。

MacOS X LionにAptanaStudio3をインストール + 日本語化

MacOSXにAptanaStudio3をインストールします。

 

AptanaStudioはEclipseをベースとしたWeb統合開発環境です。

 

EclipseにもAptanaStudioプラグインは用意されていますが、今回はスタンドアローン版のAptanaStudioを使用します。

 

現行のバージョンは3.1.3です。

■ダウンロード、インストール

f:id:satoshun00:20120628033404p:plain

まずはAptanaStudio公式サイトからパッケージをダウンロードしてください。

http://www.aptana.com/

ダウンロードが完了したらパッケージを展開し、ドラッグ&ドロップでインストールは完了です。

 

■AptanaStudioを日本語化

日本語化にはEclipse Pleiadesの日本語化パッケージを利用します。

http://mergedoc.sourceforge.jp/

・上記URLページ内の「Pleiades 本体ダウンロード」から安定版zipをダウンロードします。(現行のバージョンは1.3.4でした)

・ダウンロードしたzipを展開し、「features」「plugins」それぞれの中にあるフォルダをアプリケーション>Aptana Studio 3の中にある同名のフォルダ内にコピーします。

・次にファインダーからアプリケーション>Aptana Studio3まで行き、AptanaStudio3.appを右クリックして「パッケージの内容を表示」します。

・Contents>MacOSフォルダ内にあるAptanaStudio3.iniをテキストエディターで開いて以下の文を最後の行に追加します。

-javaagent:/Applications/Aptana Studio 3/plugins/jp.sourceforge.mergedoc.pleiades/pleiades.jar 

・アプリケーションフォルダからAptanaStudioを起動すると、ワークスペースの場所(作業フォルダ)を訪ねられるので、任意の場所に設定します。

 

※この時、これより前にAptanaStudioをインストールしたことがあると、「Log File」のようなエラーを吐いて起動できないことがあります。

その時はワークスペースの場所を変更するか、ワークスペースと同階層にある「.eclipse」という名前の隠しフォルダを削除してください。

 

正常に起動すればインストールは以上です。