2007-04-29
Opera を使っている・いた理由
OperaなFriendの話とか聞いてると「なにゆえにOpera????」って思う。悪い意味じゃなくホントに気になる今日この頃
Nothing found for 2007 04 Opera
この記事の感想
私自身は,NN→IE→Donut系→Opera (2年ほど)→Firefox と乗り換えてきて,現在は,
て感じです。
先にあげたとおり ini ファイルのカスタマイズはほとんどしないライトユーザですが,Opera を使っていた・使っている理由を列挙すると
Opera は軽い
初めて Opera を入れたころは別に軽くもないじゃんと思っていたんですが,(今,家でこれを書いているような)非力なマシンで使うと圧倒的な差が感じられます。まぁあくまで Firefox に比べてですけど,Linux だと IE ないし他の選択肢としてはこれかなという程度で。
Opera は必要十分な機能が備わっている
現在の 9 系だと,
が標準で備わっています。
家の Firefox に機能拡張をちまちま入れるのがめんどくさいですってのがあるです。
そもそも Firefox + AiO Gesture でも,進む・戻る・上位階層・閉じたタブを戻す,くらいしか enable にしないもので,Opera ので充分。タブまわりも自分好みにもともとなっているんで Firefox の TabMixLite も同じようなルックフィールになるように調整してます。ほか,細かい機能(JavaScript の機能抑制等)も自分にとっては必要充分。
ただし,このへんは刷りこみ現象かも。
理想をいうと AdBlock がもうちょっと柔軟ならいいんですが。あと RefControl も昔はいちいち F12 キーで on / off してたんですが,「サイト設定の編集」を使えばカスタマイズできることに最近気づきました。
メール機能(Opera M2)がすごい
GMail のなかったころだと SHARP の Mailist に一番コンセプトが近かった「検索・フィルタ主体メーラ」が事実上無料で使えたというのが(昔メインで使っていた)おもな理由です。正直 M2 だけ独立したクライアントがあったら今でもほしい。
でも日本語の検索フィルタ精度がいまひとつ信頼できないことがあったというのと,メールがたまってくると Opera 自身の起動に時間がかかるようになったのでメールは通常のスタンドアロンなメーラを使うようになってしまいました。今は改善されているのかもしれませんが。
そういえば最速さんも M2 を使ってらしたような…
以上,Opera を使っていた・使っている理由をあげてみました。かなり消極的な理由ばかりになってしまいましたが。
加えると IE 6 よりは CSS 等の解釈がまともそう*1(←超主観)なので css や HTML を書くなら FF 主体で書いて Opera で確認,Safari でも確認して(だって手元に Mac ないんだもん)最後に IE でもうまく見えるよう調整とか。
*1:フォームのテキストフィールド等のサイズになんか変な癖があるような気もします
2007-04-16
ClearSilver を Apache のフィルタモジュールとしてインプリメントしてみた
これまでもちらほらと ClearSilver という名前を聞いたことがあったんですが,YAPC::Asia での ikebe さんの発表でさらに興味を持ちました。
mod_livedoor_page.so
Inside Livedoor 2006-2007 - PDFへのリンクなので注意
- http://www.livedoor.com/
- use ClearSilver Template engine.
- top-page is not changed so frequently.
- top-page has simple data structure only.
- so we use C for top-page.
興味を持ちましたというか,キーくやしい(←何が?)となったので,自分でも ClearSilver を使った Apache モジュールを書いてみました。ソースはこちら。
実はググったら id:bonar さんがとっくに mod_clearsilver なるものを作ってたんで一瞬戦意が萎えかけたんですが,id:bonar さんはテンプレートファイルにたいする content handler として作ってらっしゃったので,filter として作ってみよう,と。
filter として作ると何がうれしいか,ですが,一言でいうと他のコンテンツハンドラと競合しないことです。つまり HDF コンテンツ自体は静的ファイルなり CGI なり PHP なりなんなりで出力して,それをテンプレートにあてはめることができる。ぶっちゃけ,X-SendFile みたいな使い方ができます*1。
<Location /cs_test>
Options +ExecCGI
AddHandler cgi-script cgi
SetOutputFilter CLEARSILVER
</Location>
みたいな設定をしといて,
<html><body> <p>My name is <?cs var:html_escape(MYNAME) ?></p> </body></html>
のようなテンプレートを用意しつつ
#!/usr/bin/perl print {*STDOUT} <<'END_DATA'; Content-Type: text/html X-CSF-Template: test.tmpl MYNAME = dayflower END_DATA
なる CGI を実行すると,
<html><body> <p>My name is dayflower</p> </body></html>
のように返ります。「X-CSF-Template」というカスタムヘッダを返すと BODY を HDF として解釈し,テンプレートに当てはめて出力するんだけど,そのカスタムヘッダがないと内容をそのままパススルーするです。
そんな仕様だと static なコンテンツが用意できないじゃんという奴は mod_asis と併用してください。というのはあんまりにむごいので「CSF_DefaultTemplate」という Apache 用設定ディレクティブも用意してあります。
filter として作ったといいつつ実はテンプレートハンドラ / HDF ハンドラとして使えるようにも作ってあります。わかる人ならソースを読めばわかる,と書いて突き放しておきます。
TODO は CGI Kit compatible な GET / POST DATA 等からの HDF Auto Generation あたりかなぁ。環境変数だけは ENV という HDF 変数に渡されるようにしてます。
参考になった URL
- 井上(さん)の日誌 - Apacheの話-
- Apache httpd のソース
- 特に modules/generators/mod_asis.c (一番シンプルだと思う)
- 特に modules/filters/mod_include.c
- APR / APR-Util のドキュメント / ソース
- 上記でも挙げた mod_clearsilver - ClearSilverのテンプレートルールを強制的に - bonar note
- ClearSilver C API つーか,ヘッダ / ソース嫁
まとめ
- 実は ClearSilver には Python / Perl*2 / Ruby bindings が用意されてるのでそれ使え
- Perl 系だと id:spiritloose さんが Perl binding を使ったモジュールをいくつか書いていらっしゃるのでテンプレートエンジンとして使うならそれらのモジュールを使えば吉
Fedora とかだと clearsilver-devel は静的ライブラリを提供してるくらい軽量ライブラリで,まるで Lua みたいだなとか思いながら作ってました。HDF の構造って Lua のテーブルに似てるし。
あと,ClearSilver じゃないけど Apache モジュールまわりでちょっと作ってみたいものとか思いついたんで,とっかかりとしてはよかったかな,と。
2007-04-12
mod_csfilter
カッとなって作った。詳しくは後日書く。
ビルド等は
% apxs -c -I /usr/include/ClearSilver -l neo_cs -l neo_utl -l neo_cgi mod_csfilter.c % sudo apxs -i mod_csfilter.la
みたいな感じで。
#include "apr_strings.h" #include "ap_config.h" #include "httpd.h" #include "http_config.h" #include "http_protocol.h" #include "http_log.h" #include "http_main.h" #include "http_request.h" #include "mod_core.h" #include <ClearSilver.h> #define CSF_TPL_HANDLER_NAME "clearsilver" #define CSF_HDF_HANDLER_NAME "clearsilver-hdf" #define CSF_FILTER_NAME "CLEARSILVER" #define CSF_TEMPLATE_CONFIG_TAG "CSF_DefaultTemplate" #define CSF_TEMPLATE_HEADER_TAG "X-CSF-Template" #define CSF_TEMPLATE_ENV_TAG "CSF_TEMPLATE" /********************************************************************* ClearSilver *********************************************************************/ typedef struct { HDF *hdf; CSPARSE *cs; } cs_rec; typedef struct { apr_pool_t *pool; apr_bucket_brigade *bb; } cs_render_cb_rec; static NEOERR * csf_init_cs(cs_rec *csr) { NEOERR *err; csr->hdf = NULL; csr->cs = NULL; err = nerr_init(); if (err != STATUS_OK) return err; err = hdf_init(&csr->hdf); if (err != STATUS_OK) return err; err = cs_init(&csr->cs, csr->hdf); /* Template Filters from CGI Kit */ cs_register_strfunc(csr->cs, "url_escape", cgi_url_escape); cs_register_strfunc(csr->cs, "html_escape", cgi_html_escape_strfunc); cs_register_strfunc(csr->cs, "js_escape", cgi_js_escape); cs_register_strfunc(csr->cs, "text_html", cgi_text_html_strfunc); cs_register_strfunc(csr->cs, "html_strip", cgi_html_strip_strfunc); /* TODO: error check */ return err; } static void csf_final_cs(cs_rec *csr) { if (csr) { if (csr->cs) cs_destroy(&csr->cs); if (csr->hdf) hdf_destroy(&csr->hdf); } } static int csf_setup_env_callback(void *data, const char *key, const char *value) { HDF *hdf = (HDF *) data; NEOERR *err; err = hdf_set_value(hdf, key, value); return 1; } static NEOERR * csf_setup_cs(cs_rec *csr, request_rec *r) { NEOERR *err; HDF *hdf_env; err = hdf_get_node(csr->hdf, "ENV", &hdf_env); if (err != STATUS_OK) return err; apr_table_do(csf_setup_env_callback, hdf_env, r->subprocess_env, NULL); return STATUS_OK; } static NEOERR * csf_cs_render_callback(void *data, char *s) { cs_render_cb_rec *crr = data; apr_bucket *b; char *t; size_t len = strlen(s); if (! s || ! len) return STATUS_OK; t = apr_pmemdup(crr->pool, s, len); b = apr_bucket_pool_create(t, len, crr->pool, crr->bb->bucket_alloc); if (! b) return nerr_raise_errno(NERR_IO, "bucket allocation failed"); APR_BRIGADE_INSERT_TAIL(crr->bb, b); return STATUS_OK; } static void csf_cs_log_error(request_rec *r, NEOERR *err, const char *msg) { STRING es; string_init(&es); nerr_error_string(err, &es); ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "%s%s%s", msg ? msg : "", msg ? ": " : "", es.buf); string_clear(&es); } static apr_status_t csf_cs_render_file(apr_bucket_brigade **prbb, request_rec *r, const char *template, const char *hdf_file, const char *hdf_str) { cs_rec ocsr; cs_render_cb_rec ocrr; NEOERR *err; apr_bucket_brigade *bb; apr_bucket *b; err = csf_init_cs(&ocsr); if (err != STATUS_OK) { csf_cs_log_error(r, err, "initialization of ClearSilver failed"); csf_final_cs(&ocsr); return APR_EGENERAL; } /* load HDF */ if (hdf_file) { err = hdf_read_file(ocsr.hdf, hdf_file); if (err != STATUS_OK) { csf_cs_log_error(r, err, "loading HDF failed"); csf_final_cs(&ocsr); return APR_EGENERAL; } } if (hdf_str) { err = hdf_read_string(ocsr.hdf, hdf_str); if (err != STATUS_OK) { csf_cs_log_error(r, err, "reading HDF failed"); csf_final_cs(&ocsr); return APR_EGENERAL; } } err = csf_setup_cs(&ocsr, r); if (err != STATUS_OK) { csf_cs_log_error(r, err, "setup of ClearSilver failed"); csf_final_cs(&ocsr); return APR_EGENERAL; } err = cs_parse_file(ocsr.cs, template); if (err != STATUS_OK) { csf_cs_log_error(r, err, "parsing of ClearSilver failed"); csf_final_cs(&ocsr); return APR_EGENERAL; } bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); /* TODO: error check */ ocrr.pool = r->pool; ocrr.bb = bb; err = cs_render(ocsr.cs, &ocrr, csf_cs_render_callback); if (err != STATUS_OK) { csf_cs_log_error(r, err, "rendering of ClearSilver failed"); apr_brigade_destroy(bb); csf_final_cs(&ocsr); return APR_EGENERAL; } csf_final_cs(&ocsr); b = apr_bucket_eos_create(r->connection->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); *prbb = bb; return APR_SUCCESS; } /********************************************************************* Apache *********************************************************************/ module AP_MODULE_DECLARE_DATA csfilter_module; typedef struct { const char *default_template; } csf_dir_config; typedef struct { int seen_eos; apr_bucket_brigade *bb; const char *template_path; } csf_out_ctx_t; static apr_bucket_brigade * create_error_brigade(request_rec *r) { apr_bucket_brigade *bb; apr_bucket *b; bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); /* TODO: error handling */ /* custom message is not implemented in httpd-core yet */ b = ap_bucket_error_create(HTTP_INTERNAL_SERVER_ERROR, NULL, r->pool, r->connection->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); b = apr_bucket_eos_create(r->connection->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); return bb; } static const char *get_template_path(request_rec *r) { csf_dir_config *conf = ap_get_module_config(r->per_dir_config, &csfilter_module); const char *template_path; request_rec *rr; do { /* from header */ template_path = apr_table_get(r->headers_out, CSF_TEMPLATE_HEADER_TAG); if (template_path) { apr_table_unset(r->headers_out, CSF_TEMPLATE_HEADER_TAG); break; } /* from header */ template_path = apr_table_get(r->err_headers_out, CSF_TEMPLATE_HEADER_TAG); if (template_path) { apr_table_unset(r->err_headers_out, CSF_TEMPLATE_HEADER_TAG); break; } /* from env */ template_path = apr_table_get(r->subprocess_env, CSF_TEMPLATE_ENV_TAG); if (template_path) break; /* from dir-config */ template_path = conf->default_template; } while (0); if (! template_path) return template_path; /* canonicalize relative path */ rr = ap_sub_req_lookup_file(template_path, r, NULL); if (rr && rr->status == HTTP_OK && rr->finfo.filetype != 0) { template_path = apr_pstrdup(r->pool, rr->filename); } else { /* TODO */ } if (rr) ap_destroy_sub_req(rr); return template_path; } /********************************************************************* Apache Handler *********************************************************************/ static int csf_handler_file_entity_check(request_rec *r) { apr_file_t *f = NULL; apr_status_t rv; ap_allow_standard_methods(r, MERGE_ALLOW, M_GET, M_POST, M_OPTIONS, -1); if (r->finfo.filetype == 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "File does not exist: %s", r->filename); return HTTP_NOT_FOUND; } if ((rv = apr_file_open(&f, r->filename, APR_READ, APR_OS_DEFAULT, r->pool)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "file permissions deny server access: %s", r->filename); return HTTP_FORBIDDEN; } ap_update_mtime(r, r->finfo.mtime); ap_set_last_modified(r); apr_file_close(f); ap_meets_conditions(r); return OK; } static int csf_tpl_handler(request_rec *r) { int ri; apr_status_t rv; apr_bucket_brigade *bb; if ((ri = csf_handler_file_entity_check(r)) != OK) return ri; if (r->header_only) return OK; rv = csf_cs_render_file(&bb, r, r->filename, NULL, NULL); if (rv != APR_SUCCESS) return HTTP_INTERNAL_SERVER_ERROR; rv = ap_pass_brigade(r->output_filters, bb); if (rv != APR_SUCCESS) return HTTP_INTERNAL_SERVER_ERROR; return OK; } static int csf_hdf_handler(request_rec *r) { int ri; apr_status_t rv; apr_bucket_brigade *bb; const char *template_path; if ((ri = csf_handler_file_entity_check(r)) != OK) return ri; template_path = get_template_path(r); if (! template_path) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "CSHandler: template file path is not specified"); return HTTP_INTERNAL_SERVER_ERROR; } if (r->header_only) return OK; rv = csf_cs_render_file(&bb, r, template_path, r->filename, NULL); if (rv != APR_SUCCESS) return HTTP_INTERNAL_SERVER_ERROR; rv = ap_pass_brigade(r->output_filters, bb); if (rv != APR_SUCCESS) return HTTP_INTERNAL_SERVER_ERROR; return OK; } static int csf_handler(request_rec *r) { if (! strcmp(r->handler, CSF_TPL_HANDLER_NAME)) return csf_tpl_handler(r); if (! strcmp(r->handler, CSF_HDF_HANDLER_NAME)) return csf_hdf_handler(r); return DECLINED; } /********************************************************************* Apache Filter *********************************************************************/ static apr_status_t csf_out_filter(ap_filter_t *f, apr_bucket_brigade *bb) { request_rec *r = f->r; csf_out_ctx_t *ctx = f->ctx; apr_bucket *b; /* fast exit */ if (APR_BRIGADE_EMPTY(bb)) return ap_pass_brigade(f->next, bb); /* already done */ if (ctx && ctx->seen_eos) return ap_pass_brigade(f->next, bb); if (! ctx) { /* first time initialization */ const char *template_path = get_template_path(r); if (! template_path) { /* template path is not specified, so pass-thru */ ap_remove_output_filter(f); return ap_pass_brigade(f->next, bb); } f->ctx = ctx = apr_palloc(r->pool, sizeof(*ctx)); ctx->seen_eos = 0; ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc); ctx->template_path = template_path; } b = APR_BRIGADE_FIRST(bb); while (b != APR_BRIGADE_SENTINEL(bb)) { apr_bucket *next_b = APR_BUCKET_NEXT(b); APR_BUCKET_REMOVE(b); /* remove from original brigade */ if (! ctx->seen_eos) { APR_BRIGADE_INSERT_TAIL(ctx->bb, b); } if (APR_BUCKET_IS_EOS(b)) ctx->seen_eos = 1; b = next_b; } /* given brigade to trash */ apr_brigade_destroy(bb); if (ctx->seen_eos) { /* reading stream completed */ char *hdf_str; apr_size_t len; apr_status_t rv; apr_bucket_brigade *out_bb; rv = apr_brigade_pflatten(ctx->bb, &hdf_str, &len, r->pool); apr_brigade_destroy(ctx->bb); if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "apr_brigade_pflatten() failed"); out_bb = create_error_brigade(r); return ap_pass_brigade(f->next, out_bb); } rv = csf_cs_render_file(&out_bb, r, ctx->template_path, NULL, hdf_str); if (rv != APR_SUCCESS) { out_bb = create_error_brigade(r); return ap_pass_brigade(f->next, out_bb); } /* forward brigade to next filter */ return ap_pass_brigade(f->next, out_bb); } return APR_SUCCESS; } /********************************************************************* Apache Module Config *********************************************************************/ static void *create_csf_dir_config(apr_pool_t *p, char *dummy) { csf_dir_config *result = apr_palloc(p, sizeof(csf_dir_config)); result->default_template = NULL; return result; } static const char *set_default_template(cmd_parms *cmd, void *mconfig, const char *msg) { csf_dir_config *conf = mconfig; conf->default_template = msg; return NULL; } static const command_rec csf_cmds[] = { AP_INIT_TAKE1(CSF_TEMPLATE_CONFIG_TAG, set_default_template, NULL, OR_ALL, "template file path"), {NULL}, }; static void register_hooks(apr_pool_t *p) { ap_hook_handler(csf_handler, NULL, NULL, APR_HOOK_MIDDLE); ap_register_output_filter(CSF_FILTER_NAME, csf_out_filter, NULL, 1 ? AP_FTYPE_CONTENT_SET : AP_FTYPE_RESOURCE); } module AP_MODULE_DECLARE_DATA csfilter_module = { STANDARD20_MODULE_STUFF, create_csf_dir_config, /* create per-directory config structure */ NULL, /* merge per-directory config structures */ NULL, /* create per-server config structure */ NULL, /* merge per-server config structures */ csf_cmds, /* command apr_table_t */ register_hooks /* register hooks */ };
ライセンス表記ははずしてしまいましたが,Apache に準じます。
2007-04-06
Firefox 2 で印刷すると THEAD, TFOOT がページ毎に印刷された
仕様みたら実際そういうことが考慮されてました。UA 依存な話ではありますが。
Table rows may be grouped into a table head, table foot, and one or more table body sections, using the THEAD, TFOOT and TBODY elements, respectively. This division enables user agents to support scrolling of table bodies independently of the table head and foot. When long tables are printed, the table head and foot information may be repeated on each page that contains table data.
Tables in HTML documents
ヘッダ部分以外にも本体は tbody要素, フッタ部は tfoot要素でそれぞれグループ化することができます。グループ化することによって、UA は表が一画面に収まらない場合、ヘッダ部とフッタ部を一画面内に固定し、本体部分はその間にスクロールさせて表示することが期待されます。また、印刷時に表が複数毎にまたがるような場合にも、用紙の全ページにヘッダ、フッタ部分のみを繰り返し付けることが期待されます。しかしながら、まだそのような振る舞いを行う実装をした UA はありません。
thead要素 XHTML HTML辞典
表計算的テーブルをそのまま上げるときに,よかれと思って合計値の行を TFOOT につっこんじゃうと,見た人が混乱する可能性がありますねってことで。
2007-01-17
mod_perl2 の $r->path_info 等のクセ
Apache2::RequestRec#path_info() 等を触っててもどうも安定しないというか望み通りのものが得られないなぁと思っていたんで調べてみました*1。
まずは Apache2::RequestRec のリファレンスから。
- path_info
Get/set the PATH_INFO, what is left in the path after the URI --> filename translation:
(略)
- filename
Get/set the filename on disk corresponding to this response (the result of the URI --> filename translation).
(略)
この下線部がにおいます。なので,実験。
<Location /sandbox/abc>
SetHandler perl-script # modperl でもいいけど
PerlResponseHandler Sandbox::PrintPathname
</Location>
のような設定で,$r->path_info() を出力するとどうなるのか。
http://〜/sandbox/abc/def/ghi/jkl にアクセスしたとき,
$r->path_info() == '/abc/def/ghi/jkl'
が戻ります。個人的には /def/ghi/jkl が戻って欲しいんですが。
ここで,DocumentRoot 配下に sandbox というディレクトリを掘ると,
$r->path_info() == '/def/ghi/jkl'
が戻ります(おお,望む結果だ)。
さらに,sandbox/abc を掘ると,
$r->path_info() == '/ghi/jkl'
Location も越えて sandbox/abc/def を掘ると,
$r->path_info() == '/jkl'
が戻ります。ファイルシステム上にパスが存在する限り,とことん下っていく模様です。
なので,上記のような例で SCRIPT_NAME を <Location> と同じく /sandbox/abc にしたい場合,その一個上の /sandbox まで物理的なディレクトリを掘るのがベストということで。余計に掘りすぎないよう注意。
というのもあんまりなので,代替案を考えてみました。Apache2::RequestUtil に $r->location() という関数が定義されています。こいつを使うとまさに <Location> の値を取得することができます。
じゃあこれで万事解決か,と思いきや,
<LocationMatch "/sandbox/(abc|def)">
〜
</LocationMatch>
のような設定では,$r->location() に '/sandbox/(abc|def)' という正規表現込みのデータが返ります。がーん。
というわけで,どうしても汎用的にしたい,という場合,自分でなんらかのロジックを書く必要があります。
SCRIPT_NAME を取得したい場合,
use Apache2::RequestRec (); use Apache2::RequestUtil (); my $script_name = $r->uri; my $location = $r->location; if ($script_name =~ m/ \A \Q$location\E /xms) { # <Location> タグを利用しているか # <LocationMatch> タグで正規表現を利用せずにマッチした場合 $script_name = $location; } elsif ($script_name =~ s{ ( $location ) .* \z }{$1}xms) { # <LocationMatch> タグでマッチした場合 } else { # 不明な状況なので $r->path_info を利用する my $path_info = $r->path_info; $script_name =~ s{ \Q$path_info\E \z }{}xms; }
たとえばこんなコードになるかと。
2007-01-11
C10K
なんかいろいろ読んでもやもやしたんで,自分なりにまとめてみたんですがやっぱりもにゃもにゃしたままです,すいません。
- 沢山の人がアクセスすると大変だよ(C10K)
- 特にチャットとかだと人数以上にリソース食って大変だよ
- Ajax 使ったり,通信モデルを Comet とかにするとマシになることがあるよ
- でも愚直に Apache + 現状の定番的な App サーバで実装するとリソース食って大変だよ
- Comet の場合,古典的な実装モデルより Continuation や event driven なモデルにするとリソース食わないよ
- チャットの場合は Comet + 新モデルでうまく解決できました(ぱちぱち)
- その他 C10K なこともなんらかの手段で解決できるといいよね
1-1
これがどのぐらい深刻な問題かと言うと、1000ユーザが同時接続している状態で、1秒に1度の間隔でポーリングを行うと、計算してみるとわかりますが月間で26億ヒットとなり、「なにもせずブラウザを開いているだけで」グーグルのページビュー(12億)を超えるアクセス(!!)が殺到することになります。恐ろしいですね。 CNET Japan Blog - 江島健太郎 / Kenn's Clairvoyance:Lingr and Comet - 技術解説編
3
さて、Cometならせわしない1秒に1度のアクセス・ラッシュを回避でき、必要なときだけアクションを起こすという経済的なプロトコルになるので、万事一件落着でしょうか?ところが、そうは問屋が卸しません。今度は、サーバ側に問題が発生します。 通常、Apacheなどの一般的なウェブサーバは、短い応答時間で返せる処理を大量にこなすというスループット重視の前提で設計されています。このため、リクエストを受けたらそのリクエストに対してプロセスまたはスレッドをあてがい、最後まで面倒を見るという方式が一般的です。 ところが、先ほども言ったようにCometではコネクションはつなぎっぱなしになっていますから、いつまで経ってもプロセスやスレッド(およびメモリ資源)が解放されません。しかも、それらのスレッドは仕事もせずにアイドリングしており、メモリとCPUを浪費しているだけです。これは重大な問題です。どのぐらい重大かというと、そもそも千や万のオーダーの同時接続を実現することができません。 CNET Japan Blog - 江島健太郎 / Kenn's Clairvoyance:Lingr and Comet - 技術解説編
6
@IT の件の元記事は発端が Ajax チャットなので「じゃあこれ使えばいいじゃん」という話にはなるんだろうけど、これはあくまで Comet のような実装に対しての限定的なソリューションであって、依然として C10K というか、大量コネクションに対してバックエンドのアプリケーションサーバーをどうスケーラブルにするかという問題はウェブアプリケーション開発のノウハウの中心の一つじゃないかなあと思う。 C10K - naoyaの日記
私感
ほとんどエジケンさんからの引用になってしまいました。
元記事の人は上でいう 3,6 あたりを書いていて,id:yamaz さんは 3 するなら 4 とか常識だろ,と噛みついているように読めました。むろん「Web2.0とC10Kに関する数々の誤解 - 最速配信研究会」が間違っているというわけではないんですが*1。
(2007/01/15 追記)id:yamaz さんは元記事自体をうんぬんしようとしたわけではなく,あれをきっかけとして C10K とその対策にまつわる都市伝説を払拭しようという意図だったぽいです。フォローアップ記事にてより詳細に解説なさってます。
個人的には元記事読んでも「知ったかが嘘吐いてる*2」というほどネガティブな印象はなかったです。むしろプログラマでもないライターさんが書いていること自体にびっくりしました(もちろん免罪符にはならないですが)。
おまけ(Comet with POE)
POE、とあるけど、このタイプの実装ができる PoCo だとどれだろう。PoCo::Server::HTTP の RC_WAIT と continue() を使う実装だと、結局アプリケーションサーバーがコネクションを繋ぎっぱなしな実装になるように思うけれど。 C10K - naoyaの日記
POE がよく例示されるのは,POE だと疑似マルチタスクなので fork するよりはマシだろうし,CPU 時間食わずに黙ってブロックされててくれるし,(疑似)タスク*3間でイベント投げれるし,Comet 向きだよね,って理由じゃないかなぁと思います。なので,とりあえずは,アプリサーバがコネクションを繋ぎぱなしでもいいんじゃないですか。これが単体で 10K を越えてスケールするかというと無理な気がしますが。
さらにおまけ
自分で Comet でチャット作ったときいろいろ試したんですが,IE か Firefox のどちらか(たしか IE)だと,同一サーバの別ポート宛に XMLHttpRequest を投げられたような感じでした。みんなそうならちょっとは分業しやすいんですけどね。
(2007/01/15 追記)id:tociyuki さんによるとやっぱ IE 6 だと別ポート OK だったぽいです(⇒Cometの書き込み要求は別Port、あとは…… - Tociyuki::Diary)。
2007-01-10
%uNNNN なエスケープ
一時期はてブのタイトルが「%uNNNN」の羅列になってるのがあったりしたんですけど,あれはあくまで JavaScript(1.3以降,and ECMA-262あたり?)の escape() の仕様であって JavaScript を使わなければ縁がないのでしょうか?
何を心配しているのかというと,普通の x-www-form-urlencoded / form-data なフォームで %uNNNN 形式のクエリを投げるようなブラウザ実装とかないよね?ってことなんですけど。
