我试图使用Akka HTTP进行网络服务器的POST。如果POST失败,我希望它停止并且不发送更多的POST,因为它们不是幂等的。
下面的代码创建POST并将它们发送到测试Web服务器。它会在第一个响应中抛出异常。代码应该是可运行的,在这种情况下你会看到它打印:
i = 0
got response
i = 1
stopping
Exception in thread "main" java.lang.Exception
i = 2
i = 3
i = 4
i = 5
所以'停止'在将下一个请求放在一起(i = 1
)之后发生,然后代码就会继续。
有没有人知道如果出现错误并且不再发送任何POST,如何停止流程?
(Scala 2.11.8,Akka 2.4.4)
object FlowTest {
def main(args: Array[String]) {
val stop: Supervision.Decider = {
case _ =>
println("stopping")
Supervision.Stop
}
implicit val system = ActorSystem()
import system.dispatcher
implicit val mat = ActorMaterializer()
val connectionFlow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] =
Http().outgoingConnection(host = "posttestserver.com", port = 80)
val future: Future[Done] = Source(0 to 10).map {
i =>
val uri = s"/post.php?dir=so_akka&i=$i"
println(s"i = $i")
HttpRequest(method = HttpMethods.POST, uri = uri, entity = s"data $i")
}.via(connectionFlow).mapAsync(1) {
resp =>
Unmarshal(resp.entity).to[String]
.map { str =>
println(str)
throw new Exception("") // Always fail
str
}
}.withAttributes(ActorAttributes.supervisionStrategy(stop)).runForeach(println)
Await.result(future, Duration.Inf)
}
}
答案 0 :(得分:0)
所以我认为上面的代码有两个问题。
HTTP POST不应该是流水线的。我希望Akka HTTP会等到一个POST被处理完毕,没有错误,然后发送下一个。这不会发生。
异常没有在流程中传播。因此,抛出处理代码并不会阻止Source创建更多的POST并将其发送。
所以有两个修复。
我已将withSyncProcessingLimit
上的ActorMaterializer
设置为1。这会阻止Source在处理之前发送新消息。我还必须更改.mapAsync
部分,以便现在有.map
检查状态代码和错误(如果需要),以及.mapAsync
查看响应正文。您无法查看.map
部分中的响应正文。
我添加了KillSwitch
来阻止流程。抛出异常应该具有相同的效果但不会。所以这是一个可怕的黑客,但有效。
我认为必须有更好的方法来做到这一点。使用带有HTTP POST的Akka HTTP流应该不会那么痛苦。
这是新代码。
object FlowTest {
def main(args: Array[String]) {
implicit val system = ActorSystem()
import system.dispatcher
implicit val mat = ActorMaterializer.create(
ActorMaterializerSettings.create(system).withSyncProcessingLimit(1), system
)
val connectionFlow = Http().outgoingConnection(host = "posttestserver.com", port = 80)
val source = Source(0 to 10)
val killSwitch = KillSwitches.shared("HttpPostKillSwitch")
try {
val future: Future[Done] = source.via(killSwitch.flow).map {
i =>
val uri = s"/post.php?dir=test&i=$i"
println(s"i? = $i")
HttpRequest(method = HttpMethods.POST, uri = uri, entity = s"data $i")
}
.via(connectionFlow)
.map {
resp =>
println("got response")
// if(resp.status != OK) { // always fail for testing
val e = new Exception("")
killSwitch.abort(e)
throw e
// }
resp
}
.mapAsync(1) {
resp =>
Unmarshal(resp.entity).to[String]
.map { str =>
println("got " + str)
str
}
}
.runForeach(println)
Await.result(future, Duration.Inf)
} catch {
case NonFatal(e) =>
system.terminate()
}
}
}