我需要编写一个客户端,不断轮询Web服务器以获取命令。来自服务器的响应表明命令可用(在这种情况下响应包含命令)或没有命令可用的指令,您应该触发对传入命令的新请求。
我正在试图弄清楚如何使用spray-client和Akka来做到这一点,我可以想办法做到这一点,但是没有一个看起来像是完成它的惯用方法。所以问题是:
让几个线程在同一个Web服务器上轮询传入命令并将命令传递给一个actor最明智的方法是什么?
答案 0 :(得分:1)
此示例使用spray-client,scala futures和Akka 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
。
response.onComplete {...}
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个线程中的数千个请求。
因此,通过这种方式,您的客户端将保持与服务器的连接,并在连接断开时重新连接。