Spray.io尝试
Spray 是一个开源的 REST/HTTP 工具包和底层网络 IO 包,基于 Scala 和 Akka 构建。轻量级、异步、非堵塞、基于 actor 模式、模块化和可测试是 spray 的特点。
你可以通过git@osc上面获取代码 http://git.oschina.net/for-1988/Simples/tree/master/SpraySimple
build.sbt
name := "SpraySimple" version := "1.0" scalaVersion := "2.10.3" scalacOptions := Seq("-unchecked", "-deprecation", "-encoding", "utf8") resolvers ++= Seq( "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/", "Spray repository" at "http://repo.spray.io/" ) libraryDependencies ++= { val akkaV = "2.2.3" val sprayV = "1.2.0" Seq( //"org.java-websocket" % "Java-WebSocket" % "1.3.1", "org.json4s" %% "json4s-native" % "3.2.4", "io.spray" %% "spray-json" % "1.2.5", "io.spray" % "spray-can" % sprayV, "io.spray" % "spray-routing" % sprayV, "com.typesafe.akka" %% "akka-actor" % akkaV, "com.typesafe.akka" %% "akka-testkit" % akkaV % "test", "io.spray" % "spray-testkit" % sprayV % "test", "org.scalatest" %% "scalatest" % "2.0" % "test", "junit" % "junit" % "4.11" % "test", "org.specs2" %% "specs2" % "2.2.3" % "test" ) }
plugins.sbt
logLevel := Level.Warn addSbtPlugin("io.spray" % "sbt-revolver" % "0.7.1") addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.1.2")
Spray的Spray-Can模块来提供web服务,它本身就提供了服务器功能,这样就无需其他服务器。启动Http监听只需要下面一句代码
IO(Http) ! Http.Bind(httpServer, Configuration.host, port = Configuration.portHttp)
直接借助Scala的main方法就可以启动。Spray是基于Akka构建的,所以启动需要基于Akka的ActorSystem。httpServer对象就是一个Akka的Actor。
完整的启动类:
object Server extends App with Routes { implicit lazy val system = ActorSystem("server-system") lazy val index = system.actorOf(Props[IndexActor], "index") implicit lazy val routes = { new IndexService(index)(system).route } IO(Http) ! Http.Bind(httpServer, Configuration.host, port = Configuration.portHttp) } object Configuration { import com.typesafe.config.ConfigFactory private val config = ConfigFactory.load config.checkValid(ConfigFactory.defaultReference) val host = config.getString("http.server.host") val portHttp = config.getInt("http.server.ports.http") val portTcp = config.getInt("http.server.ports.tcp") val portWs = config.getInt("http.server.ports.ws") }
Spray的spray-routing模块提供路由功能,上面可以看到启动类Server继承了Routes,这个Trait是我们自己写来处理路由部分的代码,我们先看下它的实现代码
trait Routes extends RouteConcatenation with StaticRoute with AbstractAkkaSystem { val httpServer = system.actorOf(Props(classOf[HttpServer], allRoutes)) implicit def routes: Route lazy val allRoutes = logRequest(showReq _) { routes ~ staticRoute } private def showReq(req: HttpRequest) = LogEntry(req.uri, InfoLevel) } trait StaticRoute extends Directives { this: AbstractAkkaSystem => lazy val staticRoute = path("favicon.ico") { getFromResource("favicon.ico") } ~ pathPrefix("markers") { getFromResourceDirectory("markers/") } ~ pathPrefix("css") { getFromResourceDirectory("css/") } ~ pathEndOrSingleSlash { getFromResource("index.html") } ~ complete(NotFound) }
AbstractAkkaSystem:
trait AbstractAkkaSystem { implicit def system: ActorSystem }
这里我们初始化了HttpServer对象,并定义了隐式参数routes,还有对静态资源的路由配置。在Server中,我们对routes进行了实现,加入了IndexService的实现类。在这个类中,我们实现了一个很简单的Rest功能。
HttpServer对象
class HttpServer(route: Route) extends Actor with HttpService with ActorLogging { implicit def actorRefFactory = context override def receive = runRoute(route) }HttpServer对象实现了spray.routing.HttpService 和akka.actor.Actor,
implicit def actorRefFactory = context //实现HttpService中的隐式方法 actorRefFactory
override def receive = runRoute(route) // 实现Actor的receive方法,交给HttpService中的runRoute处理
class IndexActor extends Actor with ActorLogging { override def receive = { case None => } } class IndexService(index: ActorRef)(implicit system: ActorSystem) extends Directives with Json4sSupport { implicit def json4sFormats: Formats = DefaultFormats lazy val route = pathPrefix("page") { val dir = "page/" pathEndOrSingleSlash { getFromResource(dir + "index.html") } ~ getFromResourceDirectory(dir) } ~ path("echo" / Segment) { message => get { complete { s"${message}" } } } ~ path("person") { get { complete { val person = new Person("Feng Jiang", 26) person } } } }