Spray(2)spray-can

本文详细介绍了如何使用Spray框架构建HTTP服务器,包括安装、配置、代码实现及运行服务器的基本步骤。主要内容涵盖Spray框架的基础应用,从导入依赖到配置服务器,再到实现基本的HTTP请求处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值