如何解决play framework 2.2.1中的连接超时异常问题

时间:2014-03-04 14:35:28

标签: java scala playframework playframework-2.1 playframework-2.2

我使用web serviceplay framework中呼叫scala。代码遵循Producer/Consumer模式。每次调用WS大约需要2秒钟。但是很多这样的调用都超过了120秒(这是默认的超时时间)。因此它会在120秒之后抛出异常:java.net.connectException

问题:

  1. 为什么所有呼叫的时间都被加起来,而不是单独处理它们,因此超时不会成为问题。

  2. 我通过解决此问题尝试了一种增加超时的解决方案:fixed ws.timeout 。但对我来说,这个问题仍然存在。

  3. 是线程还是并发问题?

  4. 以下是课程代码:

    class WS(sentenceList: List[String], queue: BlockingQueue[Future[Response]], filename: String) {
    
      val listofJson = new ListBuffer[(String, JsValue)]
      listofJson.clear
    
      def callWSProducer() = {
    
        sentenceList.foreach { name =>
          val data = Json.obj(
            "input_sent" -> name,
            "Filename" -> filename)
    
          val holder: Future[Response] = WS.url("http://0.0.0.0:8015/endpoint/").withHeaders("Content-Type" -> "application/json").post(data)
          implicit val context = scala.concurrent.ExecutionContext.Implicits.global
          queue.put(holder)
        }
      }
    
      def WSConsumer(): List[(String, JsValue)] = {
    
        sentenceList.foreach { name =>
    
          val result = Await.result(queue.take(), 100.second)
    
          val out = (result.json \ "sentence");
    
          listofJson += ((name, out));
        }
    
        return listofJson.toList
    
      }
    
    }
    

    我在控制台中遇到错误:

    error.txt

    编辑:

    让我让问题更清楚一点。首先,通过创建上述类的对象,从控制器(主线程)调用上述函数。上面的Json列表返回到控制器,控制器又将其返回到视图。因为我们必须返回列表,所以我们可以提出的唯一可能的方法是使用await(阻塞)机制。

    我知道代码存在线程问题,但有人至少可以指出这些问题。我们尝试的所有方法要么导致上面提到的120秒超时,要么在我们的await块中存在某种死锁时导致100秒未来超时,就像我们使用类似于此处提到的解决方案时那样:{{3} }

2 个答案:

答案 0 :(得分:0)

我很困惑为什么要阻止?我认为我们可以为此提出一个非阻塞解决方案。但首先让我们解决超时问题。对于WS,您可以使用ws.timeout属性配置时间超时。我不确定为什么这不起作用。对于application.conf文件中的实验设置为0

ws.timeout=0

这实际上是将超时设置为永久。这可能会在您的代码中引发新问题。我不确定。现在让我们看看阻塞/等待的东西。这样的事情怎么样

  object Application extends Controller {

     //using Scala global implicits is a bad practice
     //implicit val context = scala.concurrent.ExecutionContext.Implicits.global

     //use the play one or even better use a custom execution context read
     //http://www.playframework.com/documentation/2.1.0/ThreadPools
     implicit val context = play.api.libs.concurrent.Execution.Implicits.defaultContext

     def index = Action.async {
          invokeServices(send your sentence list).map { listOfPairs =>
             //do your transformation here to create the result
          }
     }
     //invokeServices hopefully capture all that you are doing inside the WS class but no blocking 
     //and mutation.  
     def invokeServices(sentenceList: List[String]): Future[List[(String, JsValue)]] = {
       val responses: List[Future[(String, JsValue)]] = sentenceList.map { name =>
          val data = Json.obj(
            "input_sent" -> name,
            "Filename" -> name)

            val response: Future[Response] = 
                WS.url("http://0.0.0.0:8015/endpoint/").withHeaders("Content-Type" -> "application/json").post(data)
            response.map(result => (name, result.json \ "sentence"))
        }
       //converts List[Future[A]] to Future[List[A]]
       Future.sequence(responses)
     }

   }

答案 1 :(得分:0)

这应该完全完成async&无阻塞。这是一个示例控制器:

def pause = Action.async {
  // between 5 and 10 seconds
  val delay = Random.nextInt(5) + 5
  Promise.timeout(Ok(delay.toString), delay, TimeUnit.SECONDS)
}

// future of the total paused time (which isn't total time)
private def allPauses: Future[Int] = {
  Future.sequence {
    for (i <- 1 to 10) yield {
      // max timeout of 15 seconds so everything should be good
      WS.url("http://localhost:9000/pause").withRequestTimeout(15000).get().map(_.body.toInt)
    }
  } map (_.sum)
}

def test = Action.async {
  allPauses.map { seconds =>
    Ok(s"Total paused time was $seconds seconds")
  }
}

使用Future.sequenceSeq[Future[A]]转换为Future[Seq[A]],然后将其映射为对结果值执行某些操作。让控制器返回Future[Result],以便它也是非阻塞的。

完整代码示例:https://github.com/jamesward/play-futures-with-timeouts