Spray(2)spray-can

Spray(2)spray-can


Prepare the Sample codes
>git clone https://github.com/spray/spray.git

>cd spray



And we can run both the 2 examples like this:

>sbt "project simple-http-client" run

>sbt "project simple-http-server" run



Everything runs fine, I will import the project into eclipse

>sbt update

>sbt eclipse



And I import these projects to my eclipse

simple-http-server

spray-can

spray-http

spray-io

spray-util



Go through the Online Documents to understand spray-can
Dependencies
spray-can depends on spray-io, spray-http, spray-util, akka-actor.



Create a learning project under easy sub system

>mkdir easyspray

>cd easyspray

>vi build.sbt

name := "easyspray"



organization := "com.sillycat"



version := "1.0"



scalaVersion := "2.10.0-RC1"



scalacOptions := Seq("-unchecked", "-deprecation", "-encoding", "utf8")



>sbt update

>sbt eclipse



Import my sample project into eclipse.



Installation
I already have a sbt project named easyspray, and I will install spray-can in that project.



make the resolvers in sbt as follow:

resolvers ++= Seq(

     "sonatype releases"  at "https://oss.sonatype.org/content/repositories/releases/",

     "sonatype snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/",

   "typesafe repo"      at "http://repo.typesafe.com/typesafe/releases/",

   "spray repo"         at "http://repo.spray.io/"

)



Add the sbt maven dependencies like this>

libraryDependencies ++= Seq(

     "io.spray"            %   "spray-io"                  % "1.1-M4",
          "io.spray"                  %   "spray-http"                                % "1.1-M4",
     "io.spray"            %   "spray-can"                 % "1.1-M4",
     "io.spray"            %   "spray-util"                % "1.1-M4",
     "com.typesafe.akka"   %%  "akka-actor"                % "2.1.0-RC1"   cross CrossVersion.full,
     "com.typesafe"        %   "config"                    % "1.0.0"

)



The most important configuration file is
reference.conf configure the server side configuration and database connection configuration.


HttpServer
Connection management

Message parsing and header separation

Timeout management

Response ordering



Basic Architecture
The spray-can HttpServer is implemented as an Akka actor, which talks to an underlying IOBridge and spawns new child actors for every new connection.



Just receive the request, and design in which case it will go, sender it to the handler when response the html.

def receive = {

    case HttpRequest(GET, "/", _, _, _) =>

      sender ! index

}



lazy val index = HttpResponse(

    entity = HttpBody(`text/html`,

      <html>

        <body>

          <h1>Say hello to <i>spray-can</i>!</h1>

          <p>Defined resources:</p>

          <ul>

            <li><a href="/ping">/ping</a></li>

            <li><a href="/stream">/stream</a></li>

            <li><a href="/server-stats">/server-stats</a></li>

            <li><a href="/io-stats">/io-stats</a></li>

            <li><a href="/crash">/crash</a></li>

            <li><a href="/timeout">/timeout</a></li>

            <li><a href="/timeout/timeout">/timeout/timeout</a></li>

            <li><a href="/stop">/stop</a></li>

          </ul>

        </body>

      </html>.toString

    )

)



Starting and Stopping
The HttpServer is a regular actor, it starts like this
// the handler actor replies to incoming HttpRequests

val handler = system.actorOf(Props[DemoService])



// create a new HttpServer using our handler and tell it where to bind to

newHttpServer(handler) ! Bind(interface = "localhost", port = 8080)



It stops like this:
context.system.scheduler.scheduleOnce(1 second span, new Runnable { def run() { context.system.shutdown() } })



I think probably this command statement will shutdown the actor  context.system.shutdown(), other codes are just try to schedule that.


Message Protocol
A running HttpServer actor understands the following command messages.
Bind
Start listening for incoming connections on a particular port.


Unbind
Revert a previous Bind.


GetStats


ClearStats


…snip…
SprayCanHttpServerApp trait
I found that I am using the latest sample codes, so I need to change my version from 1.1-M4 to 1.1-M7. So the latest version make SprayCanHttpServerApp trait work.



package com.sillycat.easyspray.external



import spray.can.server.SprayCanHttpServerApp

import akka.actor.Props



object Server extends App with SprayCanHttpServerApp {

  val handler = system.actorOf(Props[EasyRouter])

  newHttpServer(handler) ! Bind(interface = "localhost", port = 8080)

}



package com.sillycat.easyspray.external



import akka.actor.Actor

import spray.http.HttpMethods

import spray.http.HttpRequest

import spray.http.HttpResponse

import spray.util.SprayActorLogging

import spray.http.HttpBody

import spray.http.MediaTypes._



class EasyRouter extends Actor with SprayActorLogging {



  def receive = {

    case HttpRequest(HttpMethods.GET, "/", _, _, _) =>

      sender ! index

  }



  lazy val index = HttpResponse(

    entity = HttpBody(`text/html`,

      <html]]>

        <body]]>

          <h1]]>Welcome Page</h1]]>

        </body]]>

      </html]]>.toString))

}



HttpClient
…snip…


HttpDialog
…snip…


Examples
Example for stop the server and get status from the server:
package com.sillycat.easyspray.external



import scala.concurrent.duration._

import akka.pattern.ask

import akka.util.Timeout

import akka.actor._

import spray.io.{IOBridge, IOExtension}

import spray.can.server.HttpServer

import spray.util._

import spray.http._

import HttpMethods._

import MediaTypes._



class EasyRouter extends Actor with SprayActorLogging {

  implicit val timeout: Timeout = Duration(1, "sec")



  def receive = {

    case HttpRequest(HttpMethods.GET, "/", _, _, _) =>

      sender ! index

    case HttpRequest(HttpMethods.GET, "/stop", _, _, _) =>

      sender ! HttpResponse(entity = "Shutting down in 1 second ...")

      context.system.scheduler.scheduleOnce(1 second span, new Runnable { def run() { context.system.shutdown() } })

    case HttpRequest(GET, "/server-stats", _, _, _) =>

      val client = sender

      (context.actorFor("../http-server") ? HttpServer.GetStats).onSuccess {

        case x: HttpServer.Stats => client ! statsPresentation(x)

      }



    case HttpRequest(GET, "/io-stats", _, _, _) =>

      val client = sender

      (IOExtension(context.system).ioBridge() ? IOBridge.GetStats).onSuccess {

        case IOBridge.StatsMap(map) => client ! statsPresentation(map)

      }

  }



  lazy val index = HttpResponse(

    entity = HttpBody(`text/html`,

      <html>

        <body>

          <h1>Welcome Page</h1>

      <ul>

    <li><a href="/stop">Stop The Server</a></li>

    <li><a href="/server-stats">Server Status</a></li>

    <li><a href="/io-stats">IO Status</a></li>

          </ul>

        </body>

      </html>.toString))

     

  def statsPresentation(s: HttpServer.Stats) = HttpResponse(

    entity = HttpBody(`text/html`,

      <html>

        <body>

          <h1>HttpServer Stats</h1>

          <table>

            <tr><td>uptime:</td><td]]>{s.uptime.formatHMS}</td></tr>

            <tr><td>totalRequests:</td><td>{s.totalRequests}</td></tr>

            <tr><td>openRequests:</td><td>{s.openRequests}</td></tr>

            <tr><td>maxOpenRequests:</td><td>{s.maxOpenRequests}</td></tr>

            <tr><td>totalConnections:</td><td>{s.totalConnections}</td></tr>

            <tr><td>openConnections:</td><td>{s.openConnections}</td></tr>

            <tr><td>maxOpenConnections:</td><td>{s.maxOpenConnections}</td></tr>

            <tr><td>requestTimeouts:</td><td>{s.requestTimeouts}</td></tr>

            <tr><td>idleTimeouts:</td><td>{s.idleTimeouts}</td></tr>

          </table>

        </body>

      </html>.toString

    )

  )

 

  def statsPresentation(map: Map[ActorRef, IOBridge.Stats]) = HttpResponse(

    entity = HttpBody(`text/html`,

      <html>

        <body]]>

          <h1]]>IOBridge Stats</h1]]>

          <table]]>

            {

              def extractData(t: (ActorRef, IOBridge.Stats)) = t._1.path.elements.last :: t._2.productIterator.toList

              val data = map.toSeq.map(extractData).sortBy(_.head.toString).transpose

              valheaders = Seq("IOBridge", "uptime", "bytesRead", "bytesWritten", "connectionsOpened", "commandsExecuted")
              headers.zip(data).map { case (header, items) =>

                <tr><td]]>{header}:</td]]>{items.map(x => <td]]>{x}</td]]>)}</tr]]>

              }

            }

          </table]]>

        </body]]>

      </html]]>.toString

    )

  )

}



References:
http://spray.io/documentation/spray-routing/

http://spray.io/documentation/spray-can/#spray-can

http://sillycat.iteye.com/blog/1744082

http://sillycat.iteye.com/blog/1750374

http://www.scala-sbt.org/release/docs/Getting-Started/index.html



http://spray.io/project-info/maven-repository/


你可能感兴趣的:(ca)