JavaネットワークアプリケーションフレームワークNettyの紹介 | サイバーエージェント 公式エンジニアブログ
こんにちは、新規開発局のイニシャルP.です。

今回はJBoss OpenSource Projectの一つであるNetty Projectを紹介します。

Nettyは高性能・高スケーラビリティなネットワークアプリケーションを快速開発するために設計されたNIOクライアント・サーバフレームワークです。

Nettyを使うと複雑なNIO Selector APIを知らなくても簡単に非同期なイベント駆動型のネットワークアプリケーションの作成が可能になります。

簡単とはいえ、APIや使い方の説明になると楽しくないので、
ここでは実際にNettyを利用してMemcachedサーバを作ってみることにします。

動作環境について
・JDK1.5以上
・Nettyの最新バージョン(現時点で3.2.1-Final)

Memcachedサーバの実装について
・Binary Protocolのみ
・GetとSetのみ
・KeyとValueのデータタイプはStringのみ

まずはNettyの特徴について簡単に説明します。

NettyはChannel、ChannelBuffer、EventModelの3つのコンポーネントで構成されています。

NettyはP2P通信に必要なオペレーションをChannelという非同期I/Oインタフェースに抽象化します。
Javaの伝統的なI/O APIは転送タイプごとにメソッドが異なって、転送タイプが変わるとそれぞれ実装が必要ですが、Nettyでは転送タイプごとにChannelFactoryが用意されていて、異なる転送タイプへのPortingもChannelFactoryを変えるだけです。
(例:SocketとDatagramSocketは完全に違う方式でSocket I/Oを行う)

Channel間のデータの交換にはChannelBufferを使います。

Channelで発生するChannelEventはChannelPipelineのChannelHandlerによって処理されます。

ChannelHandlerにはInboundを担当するChannelUpstreamHandlerとOutboundを担当するChannelDownstreamHandlerがあります。

ChannelEventの制御はChannelPipelineを組むことでできます。
例えば、データがソケットから読み込まれるときに何をするか定義します。

それではコードをみてみましょう。
(Getter/Setterは省略)

まずはMemcached Binary Protocol Specを参考して、Protocolを定義します。
$VAXのブログ-protocol
Headerは24bytes固定サイズで、その中にBodyのサイズも含まれています。
ここではシンプルに Packet(Header, Body) で定義します。

TCP/IPのようなストリーム基盤転送では、受信データはソケット受信バッファに保存されます。
ストリーム基盤の受信データはBytesなので、それを解析してPacketに変換してくれるプロトコルデコーダが必要です。
NettyのFrameDecoderを拡張して、プロトコルデコーダを作成します。
$VAXのブログ-decoder
NettyにはさまざまなCodecが用意されています。
詳しくはNetty Projectサイトのドキュメントを参考してください。

次はデコード済みのPacketでGetやSetを行うビジネスロジックハンドラーを作成します。
$VAXのブログ-handler
キャッシュにConcurrentMapを使っていますが、コード内ThreadSafeなどは考慮されていないので、普通のMapでも構いません。
クライアントにリスポンスを返す時にもPacketをbytesに変換してくれるエンコーダが必要ですが、今回ではビジネスハンドラー内でそのままエンコードして返します。

ここまでのフローを整理すると、
1. Request(Bytes)受信
2. デコード(Bytes ---> Packet)
3. ビジネスロジック実行、Responseをクライアントに返す

上記のフローとおりにPipelineを組みます。
$VAXのブログ-pipeline_factory

mainメソッドを作成し、サーバを起動します。
$VAXのブログ-main

Memcachedクライアントを使ってテストしてみましょう。

// client : spymemcached
MemcachedClient c = new MemcachedClient(
new BinaryConnectionFactory(),
AddrUtil.getAddresses("localhost:11211"));
c.set("hello", 0, "world");
System.out.println(c.get("hello"));
c.shutdown(3000, TimeUnit.MILLISECONDS);

終わりに

完璧な実装ではないですが、たった5つのファイルでMemcachedサーバが作れました。
Nettyはシンプルですが強力なアーキテクチャを持つネットワークフレームワークです。
スレッドモデルも提供していて、カスタマイズ(SEDAなど)することも簡単にできます。
Netty Projectサイトにはさまざまなサンプルコードも用意されているので、参考になると思います。

・Netty Project Site
http://jboss.org/netty.html

・Memcached Binary Protocol
http://code.sixapart.com/svn/memcached/branches/binary/server/doc/protocol-binary.txt