Scala 持续轮询HTTP服务器并分派给参与者的惯用方法
我需要编写一个客户端,不断轮询web服务器的命令。来自服务器的响应表示命令可用(在这种情况下,响应包含该命令)或指示没有可用的命令,您应该发出新的传入命令请求 我正试图找出如何使用spray client和Akka来实现这一点,我可以想出一些方法来实现这一点,但没有一种方法看起来像是实现这一点的惯用方法。因此,问题是: 让两个线程在同一个web服务器上轮询传入的命令并将命令交给参与者,最明智的方法是什么 你可以用。它在许多Scala框架中实现。例如,在代码中如下所示:Scala 持续轮询HTTP服务器并分派给参与者的惯用方法,scala,akka,spray,Scala,Akka,Spray,我需要编写一个客户端,不断轮询web服务器的命令。来自服务器的响应表示命令可用(在这种情况下,响应包含该命令)或指示没有可用的命令,您应该发出新的传入命令请求 我正试图找出如何使用spray client和Akka来实现这一点,我可以想出一些方法来实现这一点,但没有一种方法看起来像是实现这一点的惯用方法。因此,问题是: 让两个线程在同一个web服务器上轮询传入的命令并将命令交给参与者,最明智的方法是什么 你可以用。它在许多Scala框架中实现。例如,在代码中如下所示: class SSE ext
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个线程中处理数千个请求
这样,您的客户端将保持与服务器的连接,并在连接中断时重新连接。您可以使用。它在许多Scala框架中实现。例如,在代码中如下所示:
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个线程中处理数千个请求
因此,通过这种方式,您的客户端将保持与服务器的连接,并在连接中断时重新连接。本例使用,和
实现根据所需的行为而有所不同(同时并行执行多个请求、以不同的间隔执行、向一个参与者发送响应以一次处理一个响应、向多个参与者发送响应以并行处理……等等)
此特定示例显示如何同时并行执行多个请求,然后在每个结果完成时对其执行某些操作,而无需等待同时触发的任何其他请求完成
// Executes `oneThing`, executes `andThenAnother` when `oneThing` is complete,
// then executes `finally` when `andThenAnother` completes.
for {
oneThing <- future1
andThenAnother <- future2
finally <- future3
} yield (...)
下面的代码将每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]
}
系统
需要替换为实际的Akka actor系统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
)
anActorRef!响应
response.onComplete{…}
fetch
方法和附带的spray代码即可$sbt run
,导航到0.0.0.0:9000
,并在执行sbt run
的控制台中查看代码——它应该打印Hello World\再见,世界代码>或<代码>再见世界\我的世界代码>(由于并行Future.traverse执行,顺序可能是随机的)。此示例使用,和
实现根据所需的行为而有所不同(同时并行执行多个请求、以不同的间隔执行、向一个参与者发送响应以一次处理一个响应、向多个参与者发送响应以并行处理……等等)
此特定示例显示如何同时并行执行多个请求,然后在每个结果完成时对其执行某些操作,而无需等待同时触发的任何其他请求完成
// Executes `oneThing`, executes `andThenAnother` when `oneThing` is complete,
// then executes `finally` when `andThenAnother` completes.
for {
oneThing <- future1
andThenAnother <- future2
finally <- future3
} yield (...)
下面的代码将每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]
}
系统
需要替换为实际的Akka actor系统
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可以替换为actor send:anActorRef!响应
代码示例不考虑失败——处理失败的好地方是printResponse(或等效)方法,方法是使用未来的onComplete回调:response.onComplete{…}
也许很明显,但是spray客户端可以被另一个http客户端替换,只需替换fetch
方法和附带的spray代码即可
更新:完整运行代码示例如下:
git克隆repo,签出指定的提交sha,$sbt run
,导航到0.0.0.0:9000
,并在执行sbt run
的控制台中查看代码——它应该打印Hello World\再见,世界代码>或<代码>再见世界\我的世界代码>(由于并行Future.traverse执行,顺序可能是随机的)。您不能控制服务器吗?如果是,请使用websocket和