Taste of Tech Topics

Acroquest Technology株式会社のエンジニアが書く技術ブログ

イベントドリブンで通信処理を行えるNetty 導入編

導入編 目次

こんにちは!新しい物好きなエンジニアのツカノと言います。
情報システムのインフラを支えるOSSのひとつにNettyというフレームワークがあります。

f:id:acro-engineer:20130323171817j:plain
Nettyはイベントドリブンな非同期通信を行うアプリケーションを開発するためのフレームワークで、これを使うとネットワークプログラミングを見通し良く書ける、という特徴があります。今回から数回に分けて、Nettyについて紹介します。

それでは、よろしくお願いします。

インフラOSSを使いこなそう!

ここ数年、ビッグデータ関係のプロダクトが増え、多くのサーバを利用してスケールアウトするシステムの開発が増加しています。こういう分散システムの分野では、Hadoopを代表とするビッグデータ関係のプロダクトは華やかだし、重要ではあるのですが、忘れてはならない基礎技術もあると考えています。

それは分散システムのインフラ部分を支えるプロダクトです。分散システムの開発に必要となる通信機能や、複数サーバで協調動作する仕組みを提供するOSSを、私は「インフラOSS」と呼んでいます。例えば、以前このブログでも紹介しているØMQ(通信機能を提供)は、インフラOSSのひとつと考えいてます。
自分たちで分散システムを開発する際には、「インフラOSS」を使いこなすことが必要と考えています。

そこで、数回に渡り、インフラOSSのひとつであるNettyについて書いています。

Nettyとは?

Javaで開発されているOSS(ライセンスはApache License 2.0)で、イベントドリブンな非同期通信を行うアプリケーションを開発するためのフレームワークです。ソケットを生で使っても、NIOを使っても、TCPやUDPのネットワークプログラミングは、かなりしんどいです。システム開発で求められるのはビジネスロジックなので、通信処理自体の開発には極力少なくしたいと私は考えています。

Nettyを使うと、イベントドリブンで見通し良くネットワーク処理を記述できます。NettyのWebサイトにあるRelated projectsのページを見ると、多くのOSSで利用されており、インフラOSSであるNettyの重要性を感じることができると思います。
Related projectsで紹介されている中で、個人的にはAkkaやMessagePack、Infinispanに興味があります^^

具体例を見てみましょう

では、雰囲気を感じるために、具体的なサンプルを見てみましょう。

ここでは、サンプルとしてクライアントから"Hello, World!"という文字列を受信してそのまま返すエコーサーバを紹介します。

「データの先頭に電文長があり、その後に実際の電文内容が続く」という電文プロトコルに、文字列を詰めて送信することにします。最初の4byteに電文長を格納し、残りは電文内容(文字列をbyteにしたもの)だとしましょう。とてもシンプルで、典型的な例です(以前、別のサイトでも紹介したことがあります)。

今回は初回なので、まずはサーバ側のみ紹介して雰囲気を感じてもらおうと思います(Nettyはメジャーバージョンが3から4になろうとしているのですが、ここではNetty3で記述します)。

EchoServerがメインクラスで、ここでアプリケーションの初期化を行っています。また、具体的に受信した電文を処理しているロジックを記述したのがEchoServerHandlerです。

  • EchoServer
package netty_sample;

import java.net.InetSocketAddress;
import java.util.concurrent.Executors;

import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
import org.jboss.netty.handler.codec.frame.LengthFieldPrepender;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;

/**
 * サーバ側メインクラス
 */
public class EchoServer {

    public static void main(String[] args) {
        ChannelFactory factory = 
            new NioServerSocketChannelFactory( // server
                    Executors.newCachedThreadPool(),
                    Executors.newCachedThreadPool()
                    );
        
        ServerBootstrap bootstrap = new ServerBootstrap(factory);
        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            public ChannelPipeline getPipeline() {
                ChannelPipeline pipeline = Channels.pipeline();
                // Downstream(送信)
                pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
                pipeline.addLast("stringEncoder", new StringEncoder());
                // Upstream(受信)
                pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(8192, 0, 4, 0, 4));
                pipeline.addLast("stringDecoder", new StringDecoder());
                // Application Logic Handler
                pipeline.addLast("handler", new EchoServerHandler()); // server
                
                return pipeline;
            }
        });
        
        bootstrap.bind(new InetSocketAddress(9999)); // 9999番ポートでlisten
    }
}
  • EchoServerHandler
package netty_sample;

import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;

/**
 * サーバ側アプリケーションロジック
 */
public class EchoServerHandler extends SimpleChannelHandler {
    /**
     * クライアントから電文を受信した際に呼び出されるメソッド
     */
    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent event) {
        String msg = (String) event.getMessage(); // 受信電文を取りだす
        ctx.getChannel().write(msg); // クライアントに送信
    }
}

注目すべき点は、EchoServerHandlerのmessageReceivedメソッド。
電文を受け付けると、Netty内部でStringに変換してくれるため、このメソッドが呼び出されます。このように単純なロジックでクライアントに応答を返せるのが良いですね。これ、自分で書いたらエラー処理とか結構大変です。
もっと複雑なオリジナルのプロトコルも簡単に実装できますが、それは次回以降に紹介します。

それではまた〜