CLOVER🍀

That was when it all began.

JBoss Nettyを触ってみる

以前からちょっと興味はあったのですが、NIOベースのフレームワークを触ってみることにしました。この分野でのフレームワークと言えば…

Grizzly
http://grizzly.java.net/

Apache MINA
http://mina.apache.org/

JBoss Netty
http://www.jboss.org/netty

JBoss NettyはApache MINAの中心人物が移って作られたフレームワークらしく、使い方もApache MINAによく似ているそうな。両方とも使ったことがないので、そうなんですかぁ〜って感じなのですが。

んで、どれを使ってみるかってことですが、最近はいろんなプロダクトでNettyが使われているらしく(Akkaでも使われているそうな)、今のところのデファクト?という気がするので、いったんNettyを触ってみることにしました。

これまであんまりJBoss系のプロダクトには手を出していなかったのですが(重厚そうなイメージがあるので…)、まあいいやってことで。

まずは、チュートリアルのEchoをScalaで写経してみました。
http://docs.jboss.org/netty/3.2/xref/org/jboss/netty/example/echo/package-summary.html

build.sbt

name := "netty-echo"

version := "0.0.1"

scalaVersion := "2.9.0"

organization := "littlewings"

resolvers += "repository.jboss.org" at "https://repository.jboss.org/nexus/content/repositories/releases/"

libraryDependencies += "org.jboss.netty" % "netty" % "3.2.4.Final"

EchoServer.scala

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

import org.jboss.netty.bootstrap.ServerBootstrap
import org.jboss.netty.channel.{ChannelPipeline, ChannelPipelineFactory, Channels}
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory

object EchoServer {
  @throws(classOf[Exception])
  def main(args: Array[String]): Unit = {
    val bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(
      Executors.newCachedThreadPool(), Executors.newCachedThreadPool()
    ))

    bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
      @throws(classOf[Exception])
      def getPipeline(): ChannelPipeline = Channels.pipeline(new EchoServerHandler)
    })

    bootstrap.bind(new InetSocketAddress(8080))
  }
}

EchoServerHandler.scala

import java.util.concurrent.atomic.AtomicLong
import java.util.logging.Level
import java.util.logging.Logger

import org.jboss.netty.buffer.ChannelBuffer
import org.jboss.netty.channel.{ChannelHandlerContext, ExceptionEvent, MessageEvent, SimpleChannelUpstreamHandler}

object EchoServerHandler {
  val LOGGER: Logger = Logger.getLogger(classOf[EchoServerHandler].getName)
}

class EchoServerHandler extends SimpleChannelUpstreamHandler {
  import EchoServerHandler._

  val transferredBytes: AtomicLong = new AtomicLong

  def getTransferredBytes: Long = transferredBytes.get

  override def messageReceived(ctx: ChannelHandlerContext, e: MessageEvent): Unit = {
    transferredBytes.addAndGet(e.getMessage.asInstanceOf[ChannelBuffer].readableBytes)
    e.getChannel.write(e.getMessage)
  }

  override def exceptionCaught(ctx: ChannelHandlerContext, e: ExceptionEvent): Unit = {
    LOGGER.log(Level.WARNING, "Unexcepted exception from downstream.", e.getCause)
    e.getChannel.close()
  }
}

軽く動作確認。

$ sbt "run-main EchoServer"
[info] Set current project to default (in build file:/xxxxx/netty-echo/)
[info] Compiling 1 Scala source to /xxxxx/netty-echo/target/scala-2.9.0.final/classes...
[info] Running EchoServer
$ telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Hello World!!
Hello World!!
foo
foo
bar
bar

当たり前といえば当たり前ですが、動いております。

各パラメータの意味とかは、後追いで覚えていきましょう。
この辺りが参考になりそうです。
http://d.hatena.ne.jp/fatrow/20110208/netty