2010年12月31日金曜日

Antiwebの設計 - サーバ設計

nginx、lighttpd、fhttpd、そしてAntiweb3と同じように、Antiweb4は非同期ないしイベントベースないしノンブロッキングなサーバです。つまり、複数のクライアント接続を単一のスレッドで制御します。Antiwebシステムはunixプロセスの集まりです。接続はプロセス間でsendmsg()によって転送されます。これが生じると、ソケットから最初に読まれたデータはそのソケット自身を通じて転送されます。ソケットは常に送信側のプロセスにおいて閉じられます。
ひとつのプロセスの中で多重接続を行なうため、Antiwebはsrc/libantiweb.hに定義されている状態機械データ構造を使います。Antiwebは、level-triggeredモードにおいて、kqueue()またはepoll()のいずれかのステートフルなイベントAPIを必要とします。
  • 32bit linux/CMUCLシステムにおいて、10000の非アクティヴなkeepalive接続は約3Mのユーザ空間メモリを消費した(2つのlispイメージに加えて)。
  • 非アクティヴなkeepalive接続の数は新しい接続において取るに足らない性能影響を持つ。
ファイルの送信には3つのモードがある。すなわち、中、小、大である。
  1. 中: これらのファイルはファイルのデータをユーザ空間にコピーするのを避けるためにmmap()される(メモリーマップされる)。データはファイルシステムから直接カーネルのソケットバッファにコピーされる。
  2. 小: これらのファイルはユーザ空間バッファに読まれる。というのは、小さなread()は往々にしてmmap()+munmap()より安価だからである。
  3. 大: Antiwebは大きなファイルにユーザ空間バッファを使う。これは多数の大きなファイルをクライアントが並列に要求した場合にディスクスラッシングを起こすのを避けるためであり(lighttpdからのアイデアである)、また32bitシステムでアドレス空間を使い切ってしまうのを避けるためでもある。
そいつをスーパーサイズしろ: Antiwebは64bitのoff_t型とlispの桁数無制限の整数をすべてのシステムで使っているので、Antiwebはいかなる容量のファイルでも扱うことができる。また、3つすべての送信モードにおいてダウンロード再開をサポートしている。
Antiwebのデータ構造はパイプラインのために設計されている。Antiwebはベクター型I/O(scatter-gather I/Oとしても知られている)をほとんど全面的に使っている。Antiwebの内部メッセージパッシングプロトコルもパイプラインを使っている。たとえば、ひとつのHTTP接続があって、それが小さなファイルについて2つのリクエストがあり、中くらいのファイルのリクエストが1つ続いてパイプラインされているとき、単一のwritev()システムコールで以下のように対応する。
  • 最初の2つのファイルのためのHTTPヘッダとファイルコンテント
  • 中くらいのファイルのためのHTTPヘッダ
  • カーネルバッファを満たすまで中くらいのファイルをメモリにマップする
続いて、生成されたログメッセージをすべてハブプロセスにwritev()で書き込む。ハブはログメッセージをロガープロセスに別のwritev()で転送する。最後に、ロガープロセスがメッセージをaxslogファイルに追記する。
ワーカープロセスの接続統計が見たいなら、-statsコマンドを使う。

# antiweb -stats /var/aw/example.conf
...
Keepalive Time: 65 seconds
Total Connections: 41 HTTP requests: 72 Avg reqs/conn: 1.8
File descriptor usage (estimate): 17/32767
Current Connections: 11
Keepalives: 7 Sending files to: 2
Proxy: Sources: 0 Sinks: 0 Idle: 0
Timers: 0 Hub: 1 Unix Connections: 1
Lingering: 0 Zombies: 0
...
ふだんは愛しているけれど、ときどきパイプラインは悪い。Antiwebは特定のレスポンスにおいてパーシステントなHTTP接続を切断する。
  • 4XXおよび5XX HTTPエラー - niktoのようなウェブ脆弱性スキャナがそのリクエストの95%以上をこれらのエラーとするとき、blindになるのを防ぐ。
  • ディレクトリリスト - パイプラインが再帰的にクロールするのを防ぐ。
接続を完了するとき、Antiwebはソケットと、HTTP/1.1で要求されているようにlingerの書き込みディレクションを切断する。AntiwebはHTTP/0.9およびHTTP/1.0のクライアントを常にgracefullyにデグレードする。Antiwebは第一級のIPv6サポートを持っている。もし、4XXおよび5XXエラーをパイプラインしたいなら、2つの選択肢がある。
  1. Antiwebのrewriteモジュールを使って問題のあるリクエストを実在するファイルへのリクエストに変更する。
  2. Antiwebのfast-filesモジュールを使う。これはメモリキャッシュであって静的なコンテントの加速化、HTTPヘッダの事前生成、ネガティヴキャッシュ、そして404エラーの永続化/パイプライン化をサポートしている。
Antiwebは最初からセキュリティを意識して設計されている。Antiwebの設計中に下された設計上の決定を以下に列挙する。
  • 仮想ホストはプロキシを使わずに権限を分離している。ハブが接続を扱うべきワーカーを決定すると、ハブはワーカープロセスにソケットし、その接続についてそれ以上のことは一切行なわない。ワーカープロセスはハブと異なるUIDsの下で動作する(ワーカープロセスもそれぞれ異なる)。ワーカーはオプションとしてchrootすることがある。
  • ワーカーはログファイルにアクセスしない。すべてのログメッセージはUnixソケットを通じてハブに送られる。続いて、ハブはそのメッセージをロガープロセスに送る。つまり、ワーカープロセスはそれまでに生成されたログメッセージを盗むことがないし、他のワーカープロセスによって生成されたログメッセージを盗むこともない。同様にハブはそれまでに生成されたログメッセージを盗むことがない。
  • CGIプロセスはリソース制限によって制約できる。
  • unicodeをサポートしないLispであっても、Antiwebは内部データならびにファイル名をUTF-8でエンコードする。これにはすべてのコードポイントを最短の表現となるようにし、不正なサロゲートペアがないように検証することが含まれている。(訳者注:本当だろうか? 大言壮語ではないか?)
  • Antiwebプロセスは予期せぬ状態のイベントの後始末や回復をけっして試みない。実行できなかったプロセスは失敗する。失敗しなかったプロセスは終了後に後始末される。
AntiwebにはAnti Webpagesと呼ばれるWebページを構築する実験的な技術が含まれている。これはPerlに触発されたプログラムで、意味のある空白類でページレイアウトを表現したり、HTML/CSS/Javascriptを貼り合わせたりといった機能を持つ。

Antiwebの設計 - Antiwebの新世代

Antiwebは、Hoytechによって、Common Lisp、C、そしてPerlで書かれたウェブサーバである。Antiwebは「コンセプトの検証」ではないし、「実験的なコード」でもない。Antiwebの中心的な設計は(この設計書に解説しているとおり)次の10年以上にわたって安定して使えることを意図している。
Antiweb4に強い影響を与えたウェブサーバはnginxlighttpdの2つである。Antiwebを設計するにあたって、これらやその他の優れたサーバを自由に探検させてもらった。もうひとつ影響されたサーバを挙げるなら、fhttpdがそれに挙げられる。
なぜまたウェブサーバか? われわれの意見では、上記のサーバを用いるときの最大の問題として、それらがlispで書かれていない、ということがある。われわれが学んだサーバは、拡張言語で接木されている(nginxにはPerlが、lighttpdにはLuaが採用されている)。Antiwebは違う。他の言語を使うCプログラムとするのではなく、AntiwebはC(とPerl)を使うLispプログラムなのである。

Anti Webpages - ページとレイアウト

Anti Webpageは.awpで終わるファイルである。ほとんどすべてのAntiwebのファイル同様、.awpファイルはconfsである。各.awpファイルはあなたのウェブサイトの.awp/ディレクトリへのマップである。このディレクトリには複数のページを持つことができ、それぞれはxconfで示される。以下に、1つのページxconfを持つ、簡単な.awpファイルを示す。
(page "index.html"
:layout #"
content |
"#
:content #"Hello world!"# )
  • 文字列は正規の二重引用符 (") ないし特殊な #" および "# で区切ることができる。この特殊引用符は引用符文字、バックスラッシュなどをエスケープせずに含ませることができる。
  • :layoutオプションはawpファイルのセグメントを置くためのものである。そこにはバーティカルバー (|) を終端とする行が最低一行必要になる。すべてのバーティカルバーは「並んでいる」べきである。
上記の例では、:contentセグメントがページの真ん中にある。複数のポジションを示すには複数のバーを使う。たとえば以下のコードでは:contentをページの右端に表示するように指示している。
(page "index.html"
:layout #"
| content |
"#
:content #"Hello world!"#
)
別の書き方もある。位置修正子 locator modifier (@) を使ってセグメントの位置を示すことができる。以下の例は同じ意味を持つ。
(page "index.html"
:layout #"
content@r
"#
:content #"Hello world!"#
)
@rはそのエリアの右に配置することを示す。使用可能な修正子は@r @l @t @b @tl @tr @bl @br である(ただし文字の順序は問題にしない)。やや複雑な例を示そう。
(page "index.html"
:title "Antiweb Manual"

:layout-width 800
:layout #"
logo@r | | header@l | slogan |
nav@rt | | content@tl |
footer |
"#

:header #"<h1><u>Antiweb Manual</u></h1>"#
)
上記の.awpファイルがレンダリングされると、ひとつのセグメントが表示され (:header) 、残りはNILとなる、というのはまだそれを追加していないからである。レイアウトとは関係ない特殊キーワードがある。
  • :title - このページのHTMLタイトル。
  • :css - CSSコード。複数の:cssパラメータをおいてよい。すべてHTMLページの上部のCSSブロックに追加される。
  • :js - Javascriptコード。複数の:jsパラメータをおいてよい。すべてHTMLページの上部のJavascriptブロックに追加される。
  • :js-end - :jsと同じだが、そのコードはページの上部ではなく、ページの下部のJavascriptブロックに追加される。

Anti Webpages

Anti Webpagesは、実験的で革新的な、Webコンテンツの新しい生成方法である。Antiwebのサーバ部分は、Antiweb Pagesから切り離されており、より直球である --- Antiwebサーバ自身の成し遂げていることと言えば、可能な限り効率的でセキュアにHTTP 1.1を実装しているということに尽きる。これはちょっとおかしな感じがする。Antiweb Pagesは安定していない、というのは、その実際の仕様がそのうちに変更され得るという意味においてのことである(そう近い話とも考えていないが)。
Anti Webpagesの要点は、静的なHTMLファイルを生成することであり、それもAntiwebによって非常に効率的に生成できることにある。Anti WebpagesはAJAXコールバックとフラットファイルないしBerkeleyDBデータストアをサポートするが、基本的には静的コンテンツまわりを処理することがすべてであり、このマニュアルに記載されているのがすべてである。

2010年11月23日火曜日

CMUCL

CMUCLを使って、Antiweb環境を構築しようと思う。

SBCLを使わずにCMUCLを使うのは、Doug Hoyteに敬意を払ってのことである(ミーハー)。

日本語を使う場合、SBCLの方が何かと便利なのであるが、SLIMEを使わないことを決めたので(これもDougへのオマージュ)、表面的な便利さは捨てて、ハードコアにCMUCLを使ってみることにしたのである。

おいらがGMOから借りているVirtual Private Serverは、すでにUbuntu Serverに入れ替えてある。

だから、Ubuntu Serverを自宅PC(恥ずかしながらWindows Vista 64bitである)のVirtual BoxにUbuntu Serverを入れて、そこで環境構築を練習しよう。

---

む? 64bitダメなの?

2010年10月10日日曜日

James - ユーザの追加

ユーザアカウントはサービスを横断して共有される。共通ユーザレポジトリはJamesサービスを横断して共有される。つまり、POP3メールアカウントを作成し、パスワードを設定すれば、同じアカウントがSMTPおよびNNTPの認証にも使えるということである。

アカウントを追加する前に

Jamesでは、ユーザアカウントはリモートマネージャを通じて作成される。だから、インストールが完了したら、まずリモートマネージャを設定することが、ユーザ追加の最初のステップとなる。リモートマネージャの設定についてはここを参照のこと。少なくとも1つの管理者アカウントを設定し、リモートマネージャを有効にすることが必要である。

また、ユーザを追加する前に、ユーザレポジトリの設定を正しく行なうことも必要だ。もしユーザを追加した後でレポジトリタイプをファイルからデータベースに変更すると、ユーザデータを失うことになる。これらの値の取り扱いには注意して欲しい。

これを完了させたら、Jamesを再起動して設定変更を稼働中のシステムに確実に反映させること。これでようやくユーザアカウントを作成できる準備が整った。

ひとたびJamesが起動してリスニングを始めると、ユーザの追加はきわめてシンプルである。

  1. リモートマネージャがリスニングしているホストの当該ポートにtelnetする。コマンドラインのtelnetクライアントでは、一般に「telnet 」と打鍵する。にはJamesのホスト名を、にはJamesのconfig.xmlに指定したリモートマネージャのポートを指定する。
  2. 管理者のユーザIDとパスワードのためにプロンプトが出るので、Jamesのconfig.xmlに指定した値を入力する。
  3. ログインしたら、「adduser 」と打鍵する。にはユーザ名を、にはそのアカウントのパスワードを指定する。ユーザ名には完全なemailアドレスを指定するべきではないことに注意。むしろ、すべてのemailアドレスは@の形式(にはブロックに指定した値のいずれかを指定する)がこのアカウントにデフォルトで配送されるものとして使われる。Mailetはこのデフォルトの振る舞いを変更できる。
  4. 作成したいユーザすべてについてステップ3を繰り返す。
以上です。ユーザアカウントは作成され、すべてのJamesサービスで使用可能となります。

James - グローバルサーバ設定

ひとつのコンポーネントに落とし込むことのできないグローバルな設定ブロックがいくつかあります。それらはサーバを横断してグローバルな影響を与えます。いくつかのブロックは非常に重要であり、一方で多くの場合無視されるものもありますが、もっとも洗練されたサーバ管理者にとっては重要なものです。

Jamesブロック

この設定ブロックはJamesタグで定義されます。すべての管理者はこのブロックをインストール時に調整する必要があります。属性はありませんが、いくつかの子があり、すべて必須です。

  • postmaster - この要素の中身はpostmasterのメールアドレスです。このアドレスはJamesから送信されるすべてのエラーメッセージの送信者アドレスとして使われます。また、postmaster@<サーバ名>宛てのすべてのメッセージは、このメールアドレスにリダイレクトされます。(<サーバ名>には、Jamesが取り扱っているドメイン名のいずれかが入ります。)
  • usernames - この要素は中身を持ちませんが、三つの必須の真偽値の属性を持ちます。その属性というのは、ignoreCaseenabledAliases、そしてenableForwarding.です。ignoreCaseはemailのユーザ名を大文字小文字を無視するか、無視しないかを決定します。enabledAliasesは、ローカルユーザのエイリアシングを有効にするかどうかを決めます。最後に、enableForwardingはリモートユーザへの転送を有効にするかどうかを決めます。
  • servernames - この要素はサーバがローカルとして取り扱うメールドメインとIPアドレスを定義します。二つの真偽値の属性 - autodetectおよびautodetectIPがあります。autodetectは、真にすると、サーバに自身のホスト名を確認させて、ローカルメールドメインに追加させます。autodetectIPはサーバに自身のIPアドレスを確認させて、ローカルメールドメインに追加させます。これらの属性の他に、このタグは0個以上のservername子要素を持ちます。
    • servername - 単一のホスト名またはIPアドレス。サーバがローカルとみなすメールドメインに追加される。
  • inboxRepository - これは単一の子要素を保持するシンプルなコンテナタグです。
    • repository - これはメールレポジトリを定義するもので、ローカルに配送されたメールを保持するのに使われる。この要素は中身を持たない。必須属性typeは常に"MAIL"にセットされる。必須属性repositoryURLはレポジトリを指定するのに使う(レポジトリ設定セクションを参照のこと)。

Connectionmanagerブロック

このブロックは一般的な接続管理を成業する。二つの要素がある。
  • idle-timeout - ミリ秒でクライアントの接続をタイムアウトさせるまでの時間を指定する。値を与えないと、デフォルトで5分、すなわち300000ミリ秒となる。値0はクライアントソケットをタイムアウトさせないことを意味する。
  • max-connections - max-connectionsパラメータはこの接続マネージャが許容する最大の同時接続数のデフォルトを指定する。この値は個々のサービスによって上書きできる。値を与えないと、デフォルトで30になる。値0は接続マネージャに上限を与えないことを意味するが、他のコンポーネントによるリソースの制限(たとえば最大スレッド数)が接続を開く数を制限することにはなる。

Objectstorageブロック。

低レベルのファイルレポジトリからファイルへの写像を制御するブロック。修正する必要はない。

Socketmanagerブロック

このブロックはJames内部で利用可能なソケットのタイプを制御する。SSLを有効にするのでなければ、このブロックを修正する必要はない。TLSを使う場合には修正の必要があるので、TLSの使用セクションを参照のこと。

Threadmanagerブロック

このブロックはJames内部で利用可能なスレッドプールを制御する。エキスパートの管理者だけがこの設定を修正すべきである。

2010年7月23日金曜日

Twitterクライアントを作ってみる

Twitterクライアントを作ってみることにした。
Javaで、Maven2とNetBeansを使い、TDDで。
テーマは以下のとおり。

1. Javaの道具に慣れる(今でも普通に仕事をしているけど、いまだにJavaに魅力を感じていないので)。
2. Twitter APIに親しむ(そのうちにTwitter APIを使った仕事がありそうだし)。

単にTwitterクライアントを作るだけなら、Rubyあたりがもっとも手っ取り早い。
Common Lisp(Emacs + SLIMEないしLispWorks)という手もあるね。
でも、それは会社の仕事にはつながっていかないからなあ。
Mozilla Firefox ブラウザ無料ダウンロード