libchanを読んだ

libchan を読んだのでまとめてみる。

libchanとは

libchanはdockerに使われているライブラリの1つで、先月の DockerCon で発表された。 非同期かつ一方向の通信チャネルをインプロセスでもネットワーク越しでも扱えるというGoライブラリである。 一方向とはいうものの、チャネル自体をデータに添えて他のチャネル越しに送れる。なので、返信や待ち合わせが必要ならば自分宛のチャネルを送って相手に使ってもらい、自分はそのチャネルの上で待機していれば良い。

早い話がGo言語の機能であるチャネルをネットワーク対応したようなものだ、と書いてある。

DockerはこのDockerConではDocker 1.0に加えてlibcontainer, libchan, libswarm, Docker Hubを発表していて一応キーノートの話題の1つではあったものの、 個人的にはlibswarmやKubernetesに比べるとインパクトは小さいなという感想であった。

実装

何らかのトランスポート機構の上にシリアライズされたメッセージを送る仕組みで、割とシンプルな造りである。

トランスポートとしては下記をサポートするとドキュメントに書いてある。

  • In-memory Go channel
  • Unix socket
  • Raw TCP
  • TLS
  • HTTP2/SPDY
  • Websocket

ただし、現在実装されているのは下記のみに見える。

  • In-memory Go channel
  • Unix socket
  • SPDY

また"In-memory Go channel"とはいうものの、実際にはmutexと変数共有で実装されていてchanは使っていない。

送信可能なメッセージの定義は次のようになっている。

type Message struct {
    Data    []byte
    Fd  *os.File
    Ret  Sender
}

Data は任意のペイロードRet は返信用に相手に送るチャネルである。

Ret には特別な値である libchan.RetPipe を指定できる。これを使うとチャネルの実装が勝手に返信用の逆方向チャネルを作って送ってくれるので、位置透過性を保ったまま「送信元に返信してね」と指定できる。

Fd はよく分からない。一見するとUnixソケットのFD passingみたいなものを実現するつもりなのかとも思うが、"file attachment not yet implemented in unix transport"とか書いてあるし、"file attachment"というのは何か別のものなのかもしれない。

unix.Receiver の定義が下記のようになっているあたり、やはりFD passingか、とも思うのだが。

type Receiver interface {
    Receive() ([]byte, *os.File, error)
}

感想

デザインは魅力的だ。ただし、位置づけがまだ不安定であるし、実装も開発途上で未実装機能が多い。

チャネル上でチャネルを送れるということの強力さはGoでchanを使っているとよく分かる。 それをネットワーク分散システムで利用できるのはとても楽しみだ。ただしSPDYトランスポートでのチャネル転送が libchan.RetPipe を除いて未実装なので、その魅力は現時点では半減する。

チャネル再転送の困難

実際のところリモートマシン宛の任意のチャネル転送をサポートするとなると厄介な問題に出会うだろう。 転送されてきたチャネルを再転送できるというのがこのパラダイムを強力たらしめるのだが、そのトランスポートレベルでの実装は面倒になり得る。

ノードA, B, Cがあって、Aが生成したA宛のチャネルをBに送り、BはそれをCに再転送し、Cがそのチャネルに書き込んだとしよう。素朴には2通りの実装が考えられる。

  • CはAと直接通信する。Cはチャネルを受け取った時点でAへの通信を確立し、その上に受け取ったチャネルを再現する
  • BがCとAの通信を中継する。

複雑なシステムでチャネルがあちこちでやり取りされると、中継モデルは破綻することが予想される。 チャネルの流通経路を逆に辿ってシステム内のノードをたらい回しされ、無駄に転送コストを支払うメッセージが見えるようだ。

一方で直接通信モデルはではAとCが直接通信可能とは限らない。firewall越えやNAT越えの問題があるし、Raw TCP経由でWebsocketチャネルを送った場合どっちのプロトコルが妥当とも言えない。

結局は直接通信と中継のハイブリッドが妥当であろうと考えられるが、どうハイブリッドするのか、通信パスをどう最適化するのかというルーティング問題が残る。

将来性

Dockerを実装するには便利なんだろうと思う。問題はどこまで汎用化してくれるだろうかというところにある。

一応は任意の言語でライブラリを実装可能と書いてあるので、Docker以外が使うことも想定はしているんだろう。それでも、NAT越えとかルーティングとかをどこまで真剣にサポートしてくれるのか。 その辺のDockerには必要かどうか分からない機能のためにパッチを継続的に受け付けてくれるのか。誰がそういうパッチを書くのか。

競合

多様なトランスポートをサポートした通信ライブラリという点でlibchanはあからさまにZeroMQと競合する。

中の人のコメント では「ZeroMQは機能過剰だし、ローレベル過ぎるし、要らない機能を外すのも大変」だからlibchanを作ったとある。

個人的にはこれに賛同する。チャンネル自体の転送ができるのは圧倒的な魅力だし、これで十分に強力だ。 ZeroMQがサポートするPublisher-Subscriberモデルや同期通信, 双方向通信はlibchanに欠けているが特に問題とも思わない。 バイトストリーム上にチャネル転送を構築することに比べると、PubSubや同期、双方向通信を非同期一方向+チャネル転送で実装するほうが楽だし、抽象化としてまともに思える。

しかし、皆がそう思わなければ使われないだろう。どれだけユーザー層が成熟していくのか未知数である。

結論

流行ったらいいなと思うし、ネットワーク越しのチャネル転送の仕様が固まってきたらRubyC++で実装するのもの面白いかもしれない。 でも今は使うときではないと思う。