以异步方式向akka发送请求到服务器

时间:2013-12-06 13:32:45

标签: scala asynchronous akka future

我想向涉及actor的异步服务器发出请求。说我有两个演员:

 class SessionRetriever extends Actor {
  import SessionRetriever._

  def receiver = {
    Get => 
      val s = getSessionIdFromServer() // 1
      sender ! Result(s)               // 2
  }

  def getSessionIdFromServer(): String = { ... } // 3
}

object SessionRetriever {
  object Get
  object Result(s: String)
}

并且

class RequestSender extends Actor {
  val sActor = context actorOf Props[SessionRetriever]

  def receiver = {
    // get session id
    val sesId = sActor ! SessionRetriever.Get 
    val res = sendRequestToServer(sesId)      
    logToFile(res)                            

    context shutdown sActor       
  }


  def sendRequestToServer(sessionId: String): String = { .... } 
}

我的问题:

val s = getSessionIdFromServer() // 1
sender ! Result(s)               // 2

1) getSessionIdFromServer()向服务器发出同步请求。我认为将请求异步,更正确会更好吗?因此它将返回 Future [String] 而不是普通的 String

2)如何进行异步操作:使用 AsyncHttpClient (如果我没记错其名称)或将其同步主体包装到 Future {}

3)我应该使用阻止{} 阻止吗?如果是,那么确切地说:在它的身体里面或在这里val s = blocking { getSessionIdFromServer() }

P.S。我不想在此时使用async { }await { }因为它们是相当高级别的功能,毕竟它们是在Futures上构建的。

3 个答案:

答案 0 :(得分:2)

您可以尝试这种非阻塞方式

def receive = {
    Get => 
      //assume getSessionIdFromServer() run aysnchronize
      val f: Future[String] = getSessionIdFromServer()
      val client = sender //keep it local to use when future back
      f onComplete {
        case Success(rep) => client ! Result(rep)
        case Failure(ex) => client ! Failed(ex)
      }
 }

答案 1 :(得分:1)

1)如果getSessionIdFromServer()阻塞,那么你应该从receive函数异步执行它,否则你的actor会在每次收到新请求时阻塞,并且会一直等到它收到一个新会话在处理下一个请求之前。

2)使用Future将阻塞操作“移动”到另一个线程。所以,你的演员不会被阻止,并且能够继续处理传入的请求 - 这很好 - 但是你仍然阻止一个线程 - 不是那么好。使用AsyncHttpClient是个好主意。您可以探索其他非阻塞的httpClient,例如PlayWebService。

3)我对blocking不是很熟悉所以不确定我应该在这里提出什么建议。根据我的理解,它会告诉线程池该操作是阻塞的,并且它应该生成一个临时的新线程来处理它 - 这可以避免让所有工作程序被阻塞。同样,如果你这样做,你的演员不会被阻止,但你仍然在从服务器获取会话时阻塞一个线程。

总结一下:如果可能的话,只需在getSessionIdFromServer中使用异步http客户端即可。否则,请使用Future{}blocking

答案 2 :(得分:1)

要使用AsyncHttpClient进行异步调用,您可以通过scala Promise处理java Future。

    import scala.concurrent.Future
    import com.ning.http.client.AsyncHttpClient
    import scala.concurrent.Promise
    import java.util.concurrent.Executor

    object WebClient {

      private val client = new AsyncHttpClient

      case class BadStatus(status: Int) extends RuntimeException

      def get(url: String)(implicit exec: Executor): Future[String] = {
        val f = client.prepareGet(url).execute();
        val p = Promise[String]()
        f.addListener(new Runnable {
          def run = {
            val response = f.get
            if (response.getStatusCode / 100 < 4)
              p.success(response.getResponseBodyExcerpt(131072))
            else p.failure(BadStatus(response.getStatusCode))
          }
        }, exec)
        p.future
      }

      def shutdown(): Unit = client.close()
 }

 object WebClientTest extends App {
   import scala.concurrent.ExecutionContext.Implicits.global
   WebClient get "http://www.google.com/" map println foreach (_ => WebClient.shutdown())
 }

然后通过回调来处理未来的完成。

相信Coursera的精彩反应式编程课程的代码。