我有一个函数重试,基本上看起来像这样(简化):
object SomeObject {
def retry[T](n: Int)(fn: => T): Option[T] = {
val res = try {
Some(fn)
} catch {
case _: Exception => None
}
res match {
case Some(x) => Some(x)
case None =>
if (n > 1)
//make it sleep for a little while
retry(n - 1)(fn)
else None
}
}
}
我需要在尝试之间暂停一下。据我所知,在演员中调用Thread.sleep(123)
是不可接受的:
class MyActor extends Actor {
//......
def someFunc = {
Thread.sleep(456) // it's not acceptable in an actor, there is another way to do it
}
}
显然,我不知道客户是否会在演员中使用SomeObject.retry
:
class MyActor extends Actor {
//......
def someFunc = {
SomeObject.retry(5)(someRequestToServer) // ops, SomeObject.retry uses Thread.sleep!
}
}
所以,如果我只是添加:
res match {
case Some(x) => Some(x)
case None =>
if (n > 1)
//make it sleep for a little while
Thread.sleep(123) // ops, what if it's being called inside an actor by a client?!
retry(n - 1)(fn)
else None
}
}
它不会是明智的,不是吗?如果没有,我该怎么办?
答案 0 :(得分:5)
是的,调用Thread.sleep
是一个坏主意,因为在actor系统中,线程通常是Actors之间共享的有限资源。你不希望一个Actor调用sleep并从其他Actors中占用一个Thread。
您应该做的是使用Scheduler
(请参阅docs)让您的演员在将来的某个时间向自己发送消息以重试。为此,您必须将重试代码移出SomeObject
并移至Actor
class MyActor extends Actor {
import context.system.dispatcher
def receive = {
case DoIt(retries) if retries > 0 =>
SomeObject.attempt(someRequestToServer) match {
case Some(x) => ...
case None =>
context.system.scheduler.scheduleOnce(5.seconds, self, DoIt(retries - 1))
}
}
}
然后,如果您在Actor系统之外使用SomeObject.try
def attempt(retries: Int) = {
SomeObject.attempt(someRequestToServer) match {
case Some(x) => ...
case None if retries > 0 => {
Thread.sleep(123)
attempt(retries - 1)
}
}
}
SomeObject.attempt
的位置:
object SomeObject {
def attempt[T](fn: => T): Option[T] =
try {
Some(fn)
} catch {
case _: Exception => None
}
}