我想向涉及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
上构建的。
答案 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的精彩反应式编程课程的代码。