对IO(Http) ? Http.Bind(service, interface = “localhost”, port = 8080)的理解
package com.example
import akka.actor.{ActorRef, Actor, ActorSystem, Props}
import akka.io.{Tcp, IO}
import spray.can.Http
import akka.pattern.ask
import akka.util.Timeout
import scala.concurrent.duration._
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-server")
}
class Server(service:ActorRef,implicit val system:ActorSystem) extends Actor{
implicit val timeout = Timeout(5.seconds)
IO(Http) ! Http.Bind(service, interface = "localhost", port = 8088)
def receive = {
case x:Tcp.Bound => println("Tcp.Bound="+x)
case x => println("other messages : "+x)
}
}
MyServiceActor这个类放在这里。
IO(Http)返回一个ActorRef变量,在spray.can.Http中实现, 实际上源代码如下
val manager = manager = system.actorOf(
props = Props(new HttpManager(Settings)) withDispatcher Settings.ManagerDispatcher,
name = "IO-HTTP")
所以IO(Http)返回的就是这个manager。而这个manager就是在上面代码创建的HttpManager实例。
当这个manager接收到 Http.Bind消息时,就会根据Bind内容及配置创建一个实例去监听端口。代码在spray.can中实现HttpManager的部分。如下
case bind: Http.Bind =>
val commander = sender()
listeners :+= context.watch {
context.actorOf(
props = Props(newHttpListener(commander, bind, httpSettings)) withDispatcher ListenerDispatcher,
name = "listener-" + listenerCounter.next())
}
这时HttpManager便启动了一个HttpListener实例去监听tcp端口,如果这个HttpListener实例成功绑定了tcp端口,则HttpListener会收到Tcp.Bound消息,这个Tcp.Bound消息也将会发送给上述代码中的server。注意这个Tcp.Bound消息在程序 整个运行过程中只会出现一次。除非你多次向IO(Http)发送Http.Bind消息。 此时HttpListener实例其实就是一直在监听tcp/ip层的数据, 当外部向服务器进行请求时,HttpListener实例首先会收到一个Tcp.Connected消息,然后马上创建HttpServerConnection实例,这个HttpServerConnection会将tcp层的消息封装成http层的消息。然后再发送给Http.Bind(service, interface = "localhost", port = 8080)中的service。这时service收到的消息其实已经是封闭好了的HttpRequest消息。 整个过程就是首先创建HttpManager,即IO(Http),然后向这个manager发送Http.Bind消息,创建HttpListener监听tcp层数据,一有外部连接,马上创建HttpServerConnection,该HttpServerConnection会将tcp层的数据进行封装,再以HttpRequest的形式传送给上层应用。