使用spray客户端在Actor系统内进行REST Web服务调用

时间:2014-07-15 22:31:35

标签: scala rest akka spray spray-client

我正在处理来自外部系统的连续流消息的Actor系统。我系统中有以下演员。

  1. SubscribeActor - 此actor订阅Redis频道并创建新的InferActor并将JSON有效负载传递给它。
  2. InferenceActor - 这位演员负责 2A。解析有效负载并从JSON有效负载中提取一些值文本值。 2B。调用外部REST service将2a中提取的值传递给此服务。 REST服务部署在LAN中的不同节点上,并且在计算方面做了一些繁重的工作。
  3. 使用Spray客户端调用2b中的外部REST服务。我测试了系统,它工作正常,直到2a。但是,只要我介绍2b。我开始出现OutOfMemory错误,系统最终停止运行。

    目前,我有两个主要嫌疑人 -

    1. 设计缺陷 - 我在演员中使用Spray客户端的方式     系统不正确(我是Spray的新手)
    2. 由于REST服务缓慢导致的延迟导致性能问题。
    3. 在我去#2之前,我想确保我正确使用Spray客户端,尤其是。当我从其他演员那里调用它时。我的问题是下面的用法正确/不正确/次优?

      以下是调用服务的Web服务REST客户端的代码。

      trait GeoWebClient {
        def get(url: String, params: Map[String, String]): Future[String]
      }
      
      class GeoSprayWebClient(implicit system: ActorSystem) extends GeoWebClient {
      
        import system.dispatcher
      
        // create a function from HttpRequest to a Future of HttpResponse
        val pipeline: HttpRequest => Future[HttpResponse] = sendReceive
      
        // create a function to send a GET request and receive a string response
        def get(path: String, params: Map[String, String]): Future[String] = {
      
          val uri = Uri("http://myhost:9191/infer") withQuery params
          val request = Get(uri)
          val futureResponse = pipeline(request)
          futureResponse.map(_.entity.asString)
        }
      }
      

      以下是调用上述服务的InferenceActor代码。

      class InferenceActor extends Actor with ActorLogging with ParseUtils {
      
        val system = context.system    
        import system.dispatcher    
        val restServiceClient = new GeoSprayWebClient()(system)    
      
        def receive = {
      
          case JsonMsg(s) => {
      
            //first parse the message to 
            val text: Option[String] = parseAndExtractText(s) //defined in ParseUtils trait
            log.info(s"extract text $text")
      
            def sendReq(text: String) = {
              import spray.http._
              val params = Map(("text" -> text))
              // send GET request with absolute URI
              val futureResponse = restServiceClient.get("http://myhost:9191/infer", params)
              futureResponse
            }
      
            val f: Option[Future[String]] = text.map(x => sendReq(x))
      
            // wait for Future to complete NOTE: I commented this code without any change. 
            /* f.foreach { r => r.onComplete {
              case Success(response) => log.debug("*********************" + response)
              case Failure(error) => log.info("An error has occurred: " + error.getMessage)
            }
            }
            */
            context stop self    
      
          }
        }        
      }
      

1 个答案:

答案 0 :(得分:0)

如果您的第二段代码是阻塞的,就像您说的那样,尝试在akka文档Blocking needs careful management中所述的另一个未来包装未来。

它应该限制该请求的资源量。

虽然看起来将text.map转移到不同的演员会更容易。