spray官方文档笔记之spray-can

spray-can

spray-can模块提供对客户端与服务端REST/HTTP的支持。

HTTP Server

  spray-can HTTP server是一个基于actor的嵌入式,完全异步,低层的,低开销,高性能的HTTP/1.1 的server端实现,构建于Akka IO/ spray-io之上。
支持如下特性
  • 因为每个连接的开销低,所以可以支持成千上万的并发连接
  • 能够为高吞量的应用提供高效的消息解析和高效的处理逻辑
  • 完全支持HTTP 持久连接
  • 完全支持HTTP pipelining
  • 完全支持异步的HTTP 流(即分块传输编码)
  • 可选的SSL/TLS加密
  • 基于Akka框架,可以与其他akka程序良好集成

设计哲学

spray-can HttpServer 职责是专注于实现HTTP/1.1服务器的基本功能:

  • Connection management
  • Message parsing and header separation
  • Timeout management (for requests and connections)
  • Response ordering (for transparent pipelining support)
  • 连接管理
  • 消息解析和消息头分离
  • 给 requests 和 connections用的超时管理
  • 给 transparent pipelining support使用的回复排序

            所有的HTTP servers非核心功能(如请求的路由,文件服务,压缩等)被放在了更高层,而没有在spray-can中实现。
        这样的设计使得其足够小,轻量级,易于理解和维护。这也使得spray-can可以成为一个spray-routing的完美的容器且功能互补。

        

基本结构

spray-can HTTP server由两类Akka actor实现,这两类actor都位于Akka IO之上。当你让spray-can在给定端口启动一个新的server实例时,则spray-can会启动一个HttpListener actor,这个Listener接收到来的连接请求,且给每个请求创建新的HttpServerConnection actor,然后这个actor管理这个连接的整个生命周期。这些连接actor处理http请求的连接,且将这些请求以不可变的spray-http HttpRequest实例分发给 “handler”,这个handler是由你的应用程序提供的。该handler可以仅仅对request只回复一个HttpResponse实例。如下

def receive = {
    case HttpRequest(GET, Uri.Path("/ping"), _, _, _) =>{
    sender ! HttpResponse(entity = "PONG")
    }
}
===此段由本人添加extra start===真正的handler应该如下面的service
object Boot extends App {
  // we need an ActorSystem to host our application in
  implicit val system = ActorSystem("on-spray-can")

  // create and start our service actor
  val service = system.actorOf(Props[MyServiceActor], "demo-service")

  implicit val timeout = Timeout(5.seconds)
  // start a new HTTP server on port 8080 with our service actor as the handler
  IO(Http) ? Http.Bind(service, interface = "localhost", port = 8088)
}
MyServiceActor这个类放在 这里。
===此段由本人添加extra end===

你的代码片段不会直接处理 HttpListener 和 HttpServerConnection actor,实际上是spray-can包的私有成员。所有的这些连接actor间的通信都是通过actor messages实现的,主要的messages都定义在spray.can.Http这个object中。

启动…

通过给Http extension发送Http.Bind命令来启动一个spray-can HTTP server。

import akka.io.IO
import spray.can.Http

implicit val system = ActorSystem()

val myListener: ActorRef = // ...

IO(Http) ! Http.Bind(myListener, interface = "localhost", port = 8080)

  通过Http.Bind命令你就注册了一个应用层的listener(也就是前方提到的handler:译者注),同时指定了接口和端口。另外可以通过Http.Bind命令(初始化为不同的值)来指定一些socket选项以满足你的需求。
  在HTTP层成功启动一个serve后,会有一个Http.Bound消息发送给Http.Bind 命令的发送者(即你写的handler)。
具体见更详细的 解读。

停止

显示的停止http server的方法是发送Http.Unbind给HttpListener实例。这个HttpListener实例是一开始发送Tcp.Bound的那个actor。 如下代码可以捕获这个HttpListener实例的引用。

object Boot extends App{

  // we need an ActorSystem to host our application in
  implicit val system = ActorSystem("on-spray-can")

  // create and start our service actor
  val service = system.actorOf(Props[MyServiceActor], "demo-service")

  val server = system.actorOf(Props(new Server(service,system)), "demo-serve")

}

class Server(service:ActorRef,implicit val  system:ActorSystem) extends Actor{
  var httpListener:ActorRef = null
  implicit val timeout = Timeout(5.seconds)
  val manager = IO(Http)
  manager ! Http.Bind(service, interface = "localhost", port = 8088)

  def receive = {
    case x:Tcp.Bound => println("Tcp.Bound="+x);httpListener = sender();//获取HttpListener实例引用
    case x => println("other messages : "+x)
  }
}

   如果HttpListener实例成功解绑定端口,则会发送一个Http.Unbound消息回去,有错误则发送Http.CommandFailed。之后http server就不会接收http请求了。   如果HttpListener实例在接收到解绑定命令的时候,http server还有其他的requests 没有处理完,那么会处理完后再解绑定。最后一个request处理完后,HttpListener instance会退出。你可以去监听HttpListener(方法是context.wathc(listener)) instance,然后用是否收到Terminated消息来判断HttpListener instance是否已经结束。

消息协议

成功绑定HttpListener之后,你的应用层通过许多actor message与spray-can-level层的connection actor进行通信。

Request-Response Cycle

Request-Response Cycle

Chunked Requests

Chunked Responses

Request Timeouts

Closed Notifications

Server Statistics

HTTP Headers

你可能感兴趣的:(spray)