用于连续轮询HTTP服务器并分派给actor的惯用方法

时间:2013-03-16 08:05:34

标签: scala akka spray

我需要编写一个客户端,不断轮询Web服务器以获取命令。来自服务器的响应表明命令可用(在这种情况下响应包含命令)或没有命令可用的指令,您应该触发对传入命令的新请求。

我正在试图弄清楚如何使用spray-client和Akka来做到这一点,我可以想办法做到这一点,但是没有一个看起来像是完成它的惯用方法。所以问题是:

  

让几个线程在同一个Web服务器上轮询传入命令并将命令传递给一个actor最明智的方法是什么?

2 个答案:

答案 0 :(得分:1)

此示例使用spray-clientscala futuresAkka scheduler

实现因所需行为而异(同时并行执行多个请求,以不同的时间间隔执行,向一个actor发送响应以一次处理一个响应,向多个actor发送响应以并行处理...等)。

此特定示例显示了如何同时并行执行多个请求,然后在完成时对每个结果执行某些操作,而无需等待同时触发的任何其他请求完成。

下面的代码将每隔5秒执行两次HTTP请求,同时执行0.0.0.0:9000/helloWorld和0.0.0.0:9000/goodbyeWorld。

在Scala 2.10,Spray 1.1-M7和Akka 2.1.2中测试:

处理定期作业执行的实际调度代码:

// Schedule a periodic task to occur every 5 seconds, starting as soon 
// as this schedule is registered
system.scheduler.schedule(initialDelay = 0 seconds, interval = 5 seconds) {

  val paths = Seq("helloWorld", "goodbyeWorld")

  // perform an HTTP request to 0.0.0.0:9000/helloWorld and 
  // 0.0.0.0:9000/goodbyeWorld
  // in parallel (possibly, depending on available cpu and cores)
  val retrievedData = Future.traverse(paths) { path =>
    val response = fetch(path)
    printResponse(response)
    response
  }
}

辅助方法/样板设置:

// Helper method to fetch the body of an HTTP endpoint as a string
def fetch(path: String): Future[String] = {
  pipeline(HttpRequest(method = GET, uri = s"/$path"))

}

// Helper method for printing a future'd string asynchronously
def printResponse(response: Future[String]) {
  // Alternatively, do response.onComplete {...}
  for (res <- response) {
    println(res)
  }
}


// Spray client boilerplate
val ioBridge = IOExtension(system).ioBridge()
val httpClient = system.actorOf(Props(new HttpClient(ioBridge)))

// Register a "gateway" to a particular host for HTTP requests 
// (0.0.0.0:9000 in this case)
val conduit = system.actorOf(
  props = Props(new HttpConduit(httpClient, "0.0.0.0", 9000)),
  name = "http-conduit"
)

// Create a simple pipeline to deserialize the request body into a string
val pipeline: HttpRequest => Future[String] = {
  sendReceive(conduit) ~> unmarshal[String]
}

一些注意事项:

  • Future.traverse用于并行运行期货(忽略订单)。使用对期货列表的理解将一次执行一个未来,等待每个未来完成。

    // Executes `oneThing`, executes `andThenAnother` when `oneThing` is complete,
    // then executes `finally` when `andThenAnother` completes.
    for {
      oneThing <- future1
      andThenAnother <- future2
      finally <- future3
    } yield (...)
    
  • system需要替换为您实际的Akka演员系统。

  • 在这种情况下,
  • system.scheduler.schedule每5秒执行一个任意的代码块 - 还有一个重载版本用于调度要发送给actorRef的消息。

    system.scheduler.schedule(
      initialDelay = 0 seconds,
      frequency    = 30 minutes,
      receiver     = rssPoller, // an actorRef
      message      = "doit" // the message to send to the actorRef
    )
    
  • 对于您的特定情况,printResponse可以替换为演员发送:anActorRef ! response

  • 代码示例没有考虑到失败 - 通过使用Future onComplete回调,在printResponse(或等效)方法中处理失败的好地方:response.onComplete {...}
  • 也许很明显,但喷雾客户端可以替换为另一个http客户端,只需替换fetch方法和随附的喷码。

更新:完整运行的代码示例为here

git克隆repo,签出指定的提交sha,$ sbt run,导航到0.0.0.0:9000,然后在控制台中查看执行sbt run的代码 - 它应该打印{{ 1}} OR Hello World!\n'Goodbye World!(由于并行的Future.traverse执行,顺序可能是随机的)。

答案 1 :(得分:0)

您可以使用HTML5 Server-Sent Events。它在许多Scala框架中实现。例如,xitrum代码如下所示:

class SSE extends Controller {
  def sse = GET("/sse") {
    addConnectionClosedListener {
      // The connection has been closed
      // Unsubscribe from events, release resources etc.
    }

    future {
        respondEventSource("command1")
        //...
        respondEventSource("command2")
        //...
    }
 }

SSE非常简单,不仅可以在浏览器中用于任何软件。 Akka集成在xitrum中,我们在类似的系统中使用它。但它使用netty作为异步服务器,它也适用于处理10-15个线程中的数千个请求。

因此,通过这种方式,您的客户端将保持与服务器的连接,并在连接断开时重新连接。