抛出异常时,Akka HTTP流不会停止

时间:2016-05-09 16:17:30

标签: scala akka akka-stream akka-http

我试图使用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)
  }
}

1 个答案:

答案 0 :(得分:0)

所以我认为上面的代码有两个问题。

  1. HTTP POST不应该是流水线的。我希望Akka HTTP会等到一个POST被处理完毕,没有错误,然后发送下一个。这不会发生。

  2. 异常没有在流程中传播。因此,抛出处理代码并不会阻止Source创建更多的POST并将其发送。

  3. 所以有两个修复。

    1. 我已将withSyncProcessingLimit上的ActorMaterializer设置为1。这会阻止Source在处理之前发送新消息。我还必须更改.mapAsync部分,以便现在有.map检查状态代码和错误(如果需要),以及.mapAsync查看响应正文。您无法查看.map部分中的响应正文。

    2. 我添加了KillSwitch来阻止流程。抛出异常应该具有相同的效果但不会。所以这是一个可怕的黑客,但有效。

    3. 我认为必须有更好的方法来做到这一点。使用带有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()
          }
        }
      }
      
相关问题