Archive for the ‘サーバ関連’ Category

Apacheのモジュール内で、要求されたファイルに対するmimeタイプが決まるのはどこか?

基本的に、mimeタイプは type_checkerフィーズできまる。
そのmimeタイプを決めているのが、mod_mimeであり、mod_mimeでは、

AddType や、TypesConfigなどで指定したmime typeを拡張子を基に判断している。

static void register_hooks(apr_pool_t *p)
{
    ap_hook_post_config(mime_post_config,NULL,NULL,APR_HOOK_MIDDLE);
    ap_hook_type_checker(find_ct,NULL,NULL,APR_HOOK_MIDDLE);
    /*
     * this hook seems redundant ... is there any reason a type checker isn't
     * allowed to do this already?  I'd think that fixups in general would be
     * the last opportunity to get the filters right.
     * ap_hook_insert_filter(mime_insert_filters,NULL,NULL,APR_HOOK_MIDDLE);
     */
}

そしてfind_ctの中で、ap_set_content_typeを使って決定されたmime typeを設定している。

modules/http/http_protocol.c
にある、ap_set_content_typeは、以下のようになっている。

AP_DECLARE(void) ap_set_content_type(request_rec *r, const char *ct)
{
    if (!ct) {
        r->content_type = NULL;
    }
    else if (!r->content_type || strcmp(r->content_type, ct)) {
        r->content_type = ct;

        /* Insert filters requested by the AddOutputFiltersByType
         * configuration directive. Content-type filters must be
         * inserted after the content handlers have run because
         * only then, do we reliably know the content-type.
         */
        ap_add_output_filters_by_type(r);
    }
}

従って、type_checkerの後のフェーズである。fixupsフェーズであれば、
r->content_type
から、要求したファイルに対するmime typeが取得できる。

もちろん、ここでファイルに対するcontent_typeなので、プログラムで出力した結果のcontent-typeは何も反映しない。
AddType text/html .php
とやっていれば、phpの拡張子の場合にはこの時点では
text/html
という訳だ。

ちょっと、mod_rewriteがどのようにしてURLを書き換えているのかを調べてみた。
http://net-newbie.com/trans/mod_rewrite.html

の「API フェーズ」にもあるように、また、プログラムを見ればすぐわかることだが、
mod_rewriteはtranslate_nameのフェーズかfixupのフェーズかを使っている。

以下、mod_rewriteのhook処理を登録している部分。

static void register_hooks(apr_pool_t *p)
{
    /* fixup after mod_proxy, so that the proxied url will not
     * escaped accidentally by mod_proxy's fixup.
     */
    static const char * const aszPre[]={ "mod_proxy.c", NULL };

    /* check type before mod_mime, so that [T=foo/bar] will not be
     * overridden by AddType definitions.
     */
    static const char * const ct_aszSucc[]={ "mod_mime.c", NULL };

    APR_REGISTER_OPTIONAL_FN(ap_register_rewrite_mapfunc);

    ap_hook_handler(handler_redirect, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_pre_config(pre_config, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_post_config(post_config, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_child_init(init_child, NULL, NULL, APR_HOOK_MIDDLE);

    ap_hook_fixups(hook_fixup, aszPre, NULL, APR_HOOK_FIRST);
    ap_hook_translate_name(hook_uri2file, NULL, NULL, APR_HOOK_FIRST);
    ap_hook_type_checker(hook_mimetype, NULL, ct_aszSucc, APR_HOOK_MIDDLE);
}

どうして、2つのフェーズを利用しているかがはじめわからなかったが、
自分がどのフェーズでURLの書き換えをしなければいけないかを真面目に考えていき、
さらに、上のサイトの説明を読むようになって、まあ、理解が進んだ。

要するに、同じサーバ内で見るプログラム・ファイルレベルであればtranslate_nameのレベルでできる範囲がよい。
こうすることで、単純なファイル名決定ということで解決ができ、ほぼパフォーマンスに影響がなくできる。

.htaccessなんかで設定している場合には、ここで制約・ルールが変更されるケースがあるから、
translate_nameではこの制約・ルールが取り込まれた後の処理に対応することができるないので、fixupsで処理をする。
なので、同じサーバ内で見えるプログラム・ファイルであっても、
サブリクエストとして扱って、同じようなループをもう1回、回してあげることでApacheの設定を取り込んでいく。
つまり、そのプロセス内でLocationヘッダと同じ処理が動いてしまう。

接続が一度切れるという意味ではなくあくまでも内部なので、
それほど大きな影響でもないが、ファイル名の変換レベルの手軽さというレベルでもない。
ということだ。

ただ、実際、それよりもより影響がおおきな、mod_proxyを使って内部リダイレクトを使ってもパフォーマンスに影響がでるケースを聞いたことがないから、
ほぼ影響なしといってもいいかもしれないが・・・・
(プログラマにとってはという意味。プログラム自体がこの影響よりも数桁大きな負荷を作っているから、ということで・・・)

ただ、こんな感じに見ていくとやっと、Apacheのサブリクエストの意味がしっくりきてきた。
Apache外部にはそれほど関係がないが、Apache内部では意味合いが大きくことなるということだ。
これは、Apache内部を見る必要がある場合には重要だろうということもより見えてきた。

はじめに

今はまさに、クラウドの時代が到達しつつある。
メールなどは私もgmailを使っているように、コンシューマレベルではある意味クラウドを利用している。
そして、今後その流れはより加速されていくだろう。
この流れは、ビジネスの領域、つまり会社内にも少なからず影響が出てくることは間違いない。

クラウド導入での問題点

しかしながら、クラウド導入でいいことばかりだろうか?
特に、コンシューマレベルと同じように企業内の業務も浸透するにはちょっと無理を感じる。
cloud_1

そもそも、なぜクラウドは安いのか?
それは、基本的にはあいている共用リソースを足りない部分に補えるからだ。
そして、ある使い方(時間)では、余ることが想定されているからだ。
また、同じ事を同じレベルで提供するために、規模のビジネスが通用する。

ここで、もっとも恩恵を受けるのは、CPUという計算コストだ。
これについては、全く異論はない。

しかし、データという事であればどうであろうか?

いやいや、データはgmailは8Gなのに、会社は1Gもないよ!
という事もあるのはあるが、そもそもデータがハードディスクだと言い切れば、同じようにクラウドは安い。でも本当にそれだけだろうか?

つまり、データのコストはハードディスクの容量だけではないのだ。
企業では、データを持つことで同時に以下のようなコストも負担している。
 セキュリティコスト
 他システム、サービスとの連携
 回線料(ローカルにないということはそれだけ、回線も太くなくてはならなくなる。)

という具合だ。
まあ、回線はどうなるかはわからないが、現在企業が支払っているコストでセキュリティはものすごーく高い。
インターネットを見せるメリットよりも、見せないメリットの方が大きいと判断している企業だって多々ように、誰もがデータをいつでも見えるという事に価値があるとは思っているわけではなく、いつでも見えるという問題から、見えなくするというコストを支払ってる金額も相当なものなのだ。ただ、便利なサービスになれてしまったコンシューマが、いざ、会社の中の企業人になったときのギャップは相当なものになるだろう。

ぐちぐち言っていても仕方がない。
エコポイントの情報だってクラウドなんだぜ!
今更なにいってんだ!どんどん情報も海外にながしちまえ!

というのは、受け入れてもらえるだろうか?
また、みんながそれでいいのだろうか?

3層構造という思い込み

でも、それって、こういう技術構造を思い描いているからではないだろうか?
3layer

だから、データも一緒にクラウドに・・・という発想になる。
でも、ロジック(CPU)処理が激安になるのだったら、この構造変えてもいいんじゃないでしょうか?

3相構造

私が思うのは、部分的(同じネットワーク空間の制限内のもの)には3層構造であることは変える必要が無いと思う。
がしかし、
cloud_2

こんな3相(同じ立ち位置という意味で「相」)構造がいいのではないだろうか?
なんじゃこりゃ?と思った方もいるかもしれないが、実はこのような実装はあながちあり得ない話でもなく、ある場所では結構増えてきている。
その場所とは大規模サイトのようなところだ。
ただし、表示・ロジック・データすべてが純粋ではいない。(かなり汚れている!?)

データは計算に必要な部分だけに分解され計算されている。
そして、表示の時に、その計算結果とデータをつきあわせて表示を行っているのだ。

このような実装を見ると、「基本がなっていない」という思う方もいるかもしれないが、いろいろな事情でそうなのだ。基本は基本だ。目的と前提が変われば、それを応用していかなければならない。
たとえば、データを守るDBはOracleであっても、計算上必要なデータを一時的に保持するサーバはMySQL(PostgreSQL)とような感じだ。
これは、クラウドの問題を見据えてそうなったわけでもないが、DBという値段を考えると必要な場所に必要なだけのコストしかかけられないという経済力が加われば自然とそうなる。そして、必要なデータ以外はいらないと・・・
これを研ぎ澄ましていけばいくほど、「3相構造」に近くなる。

それにオブジェクト指向って、必要なデータと必要な処理の固まりなわけで、オブジェクトとして自立して機能してほしいと考えたら自然な流れともいえなくもないだろうか?

クラウドにおける「3相構造」

この大規模サイトのテクニックの「3相構造」は、クラウドによって社内サーバが縮小していくなかで、より広い範囲で重要性を増すと私は思っている。
クラウドにより、最小限までデータ保存の責任は小さくできる。極端な話、クライアントの端末にあればそれでいいという事だっておかしくない。
(PtoPで、みんなで持ってしまえばバックアップだって万全だ。)

こんな感じだ
cloud_3

つまり・・・

だからクライアント型RIAに価値がある。と思っている。
この表示の為の合成と、計算の為の分解を行う為にはクライアント上でアプリケーションが動く方が都合がいい。
入力されたときと、出力されたときを制御するのは人だ。それを検知できるのはやはり、そのツールだ。そして、データを見るときに正しければそれでいい。
それが管理を安くするためにも必要なのだ。
(だれも見ていない深夜のデータまでも、正しいのかを確認するのはかなり高いコストがかかる。)

計算はクラウドによって安くなった。バッチ処理なんていう概念は必要ない。
いつだって、差分を計算していけばいい。間違いに気づけば、その間違った時点から計算し直せればいい。

従って、クラウドがある程度浸透したら、こんなクラウドのサービスとクライアント型RIAがセットになったサービスが出てくるだろう・・・と思っている。

と、数年前から何となく思っていたことが、もう少し表現できるようになってきたので私のイメージを書いてみました。
具体的には、AIRだけでは足りない気がしてきたので、前回と前々回の
Flashとローカルマシンとの共存
Flashとローカルマシンとの共存 ( XMLSocketを使ってみる)
なんかのような感じでAIRを補完していければ私のイメージに近づけるかも・・・という気がしてきた。

昨日の続きであるが、ローカルマシンとの接続はXMLSocketを使うことにしてみた。
とりあえず、これで思うようにできるのか、試してみることにした。

XMLSocketは、AIR以外でも使えるSocket通信だ。
いろいろ、セキュリティ関係はみれていないので、まだまだよくわからないところもあるが・・・

XMLというとなんか、定義されたルールがあるのかと思ったら、
要するに、テキストしか送受信できないSocket通信のようだ。

また、サーバのフレームワークにはnettyを使うことにした。
どうやら、MINAとnettyを作った(ている)人は同じ人らしいが、現在はnettyの方をやっているらしい

ここでは、nettyについて詳しく述べないが、夜にちょっと調べたレベルでも、簡単なサーバが作れる。
実際の受信して、返信するレベルの実装は、こんな感じだ。
(ただ、まだまだ意味がわからず書いているところも多少あるが、どれだけ簡単なイメージかはつかめるかと思う)

package com.coltware.airboost.netty.flash;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipelineCoverage;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;

@ChannelPipelineCoverage("all")
public class FlashXMLSocketServerHandler extends SimpleChannelUpstreamHandler {

	private static Log log = LogFactory
			.getLog(FlashXMLSocketServerHandler.class);

	@Override
	public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)
			throws Exception {
		log.debug("channelConnected ... " + e.toString());
		e.getChannel().write("hello\0");
		super.channelConnected(ctx, e);
	}

	@Override
	public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
			throws Exception {
		log.debug(e.getMessage().getClass());

		String request = (String) e.getMessage();

		log.debug("request: " + request);

		String response = "say what!?\0";

		ChannelFuture future = e.getChannel().write(response);
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
		log.warn("Unexpected exception from downstream.", e.getCause());
		e.getChannel().close();
	}

}

これは、Flashの方から、接続すると、「hello」が返され、何かメッセージを送ると、「say what!?」と返すだけのサーバだ。
少なくとも、これで、merapiのサンプルで書かれていたレベルの基盤はほとんどできてしまっている。

通常、サービスを止める場合には、シグナルを送る(Unix/Linux)とか、管理ポートを開いてそこで、管理コマンドを受けるとかあるが、
私は、JMXを利用した。

後は、送る文字列と送信する文字列をAMFベースにしてしまえばいいわけだ。
ただ、バイナリが送れるかわからないので、そのあたりはbase64してしまえばいい。

また、nettyのいいところは、httpの通信を標準でサポートしているので、httpベースのサーバも簡単に作れるので、
もし、XMLSocketがだめなら、別の通信と、通信プロトコルは好きに定義すればいい。

XMLを投げて、結果をXMLでもらうような形ができてきました。
もちろん、サーバアプリはAMFで処理するようになっています。
サンプルなので、非常に簡単に、引数を3つとり、1つめはそのまま出力。2つめと3つめを足し算というすごーく簡単なやつです。

< ?php
require_once("Zend/Amf/Server.php");

$server = new Zend_Amf_Server();
$server->setClass('Foo');
$response = $server->handle();

echo $response;

class Foo{
	public function hello($str1,$num1,$num2){
		$sum = $num1 + $num2;
		return $str1."さん。こんにちは! ".sprintf("%d + %d = %d",$num1,$num2,$sum);
	}
}
?>

html

と画像でなんなのですが、入力に対して結果もXMLで出ます。
(まだまだ、このサーバで動くようにするには危険があるもので・・・・)

上の「実行する」をおすと、prototype.jsを使ってContentTypeを変えてそのままのXMLをPOSTします。
$F(’input’)には、入力のXMLが入ります。
[js]
var req = new Ajax.Request(
url,{
method: “POST”,
requestHeaders:['Content-Type','application/xml+amf'],
postBody: $F(’input’),
onFailure:on_failure,
onSuccess:on_success
}
);
[/js]

んー、もうちょっと、結果のXMLをツリーで見せるととかしないとちょっときついなーーーーー

RSS
Add to Google

カスタム検索
ソフトウェア&ライブラリ


ライブラリ
airxmail(en)
AIR版メール送受信ライブラリ
airxzip
AIR版ZIP圧縮・解凍ライブラリ
カレンダー
2010年9月
« 8月    
 12345
6789101112
13141516171819
20212223242526
27282930  
アーカイブ
にほんブログ村 IT技術ブログへ