以前からちょっと興味はあったのですが、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