Java 推迟邮件的最佳方式是什么?
我有一个actor“ItemProvider”,它可以接收“getItems”消息。 ItemProvider管理项目的项。因此,我可以有几个“getItems”消息请求项目A的项目,以及其他“getItems”消息请求项目B的项目 “itemProvider”第一次收到这样一条消息时,它需要调用一个服务来实际获取项目 (这可能需要一分钟,因为服务返回一个未来,所以它不会阻止参与者)。在此等待期间,其他“getItems”消息可能会到达 项目“ItemProvider”缓存从服务接收的“Items”。 因此,在1分钟的装载时间后,它可以立即为物品提供服务 我很确定“ItemProvider”应该使用Akka的Been功能。但是,它应该如何处理它不能立即服务的客户呢 我可以想到以下几种选择:Java 推迟邮件的最佳方式是什么?,java,scala,akka,Java,Scala,Akka,我有一个actor“ItemProvider”,它可以接收“getItems”消息。 ItemProvider管理项目的项。因此,我可以有几个“getItems”消息请求项目A的项目,以及其他“getItems”消息请求项目B的项目 “itemProvider”第一次收到这样一条消息时,它需要调用一个服务来实际获取项目 (这可能需要一分钟,因为服务返回一个未来,所以它不会阻止参与者)。在此等待期间,其他“getItems”消息可能会到达 项目“ItemProvider”缓存从服务接收的“Item
客户端应按计划重新发送幂等请求,直到收到满意的答复或超时为止 您是否需要更多的ItemProvider,或者ItemProvider批处理请求取决于所查询资源的性质。如果每分钟只能发出1个请求,则应在ItemProvider中对请求进行批处理。但是,客户有责任确保其继续要求答复,直到满意为止。它不应该依赖ItemProvider来可靠地记住请求。看看Akka的()。 下面是(未测试)代码,用于在从服务器请求实际项目时隐藏getItems消息,然后在服务器请求完成后处理所有getItems消息
import akka.actor.{Actor, Stash}
class ItemProviderActor extends Actor with Stash {
private[this] itemsOpt : Option[Items] = None
def receive = processing
def processing: Receive = {
case m:GetItems => {
if(itemsOpt.nonEmpty) {
// respond immediately
itemsOpt.foreach(sender() ! _)
}
else {
// Stash current request and initiate cache update
context.become(retrivingData)
stash()
// Will send future results of item retrieval as a message to self
retrieveItems().pipeTo(self)
}
}
}
def retrivingData: Receive = {
case m: Items =>
// items are retrieved, update cache
itemsOpt = Option(m)
// resume normal processing
context.become(processing)
// put all pending item requests back to actor's message queue
unstashAll()
case m:GetItems =>
// busy retrieving items, store request to serve later
stash()
}
def retrieveItems() : Future[Items] = {
???
}
}
下面,您将找到一种可能的方法来组织参与者以满足您的需求。在这个解决方案中,我将为每个项目使用一个actor实例来缓存特定于该项目的项。然后,我将使用一个路由参与者,它将接收获取项目项的请求,并委托给处理该项目缓存的正确子参与者。在实际的缓存参与者中,您将看到我使用了stash/unstash来处理延迟请求,直到要缓存的项被加载(我在代码中模拟了这一点)。代码如下:
import akka.actor._
import scala.concurrent.Future
import akka.pattern._
import concurrent.duration._
import akka.util.Timeout
class ItemProviderRouter extends Actor{
import ItemProvider._
def receive = {
case get @ GetItems(project) =>
//Lookup the child for the supplied project. If one does not
//exist, create it
val child = context.child(project).getOrElse(newChild(project))
child.forward(get)
}
def newChild(project:String) = {
println(s"creating a new child ItemProvider for project $project")
context.actorOf(Props[ItemProvider], project)
}
}
object ItemProvider{
case class GetItems(project:String)
case class Item(foo:String)
case class LoadedItems(items:List[Item])
case object ClearCachedItems
case class ItemResults(items:List[Item])
}
class ItemProvider extends Actor with Stash{
import ItemProvider._
//Scheduled job to drop the cached items and force a reload on subsequent request
import context.dispatcher
context.system.scheduler.schedule(5 minutes, 5 minutes, self, ClearCachedItems)
def receive = noCachedItems
def noCachedItems:Receive = {
case GetItems(project) =>
stash()
fetchItems(project)
context.become(loadingItems)
case ClearCachedItems =>
//Noop
}
def loadingItems:Receive = {
case get:GetItems => stash
case LoadedItems(items) =>
println(s"Actor ${self.path.name} got items to cache, changing state to cachedItems")
context.become(cachedItems(items))
unstashAll()
case ClearCachedItems => //Noop
}
def cachedItems(items:List[Item]):Receive = {
case GetItems(project) =>
sender ! ItemResults(items)
case ClearCachedItems =>
println("Clearing out cached items")
context.become(noCachedItems)
case other =>
println(s"Received unexpected request $other when in state cachedItems")
}
def fetchItems(project:String){
println(s"Actor ${self.path.name} is fetching items to cache")
//Simulating doing something that results in a Future
//representing the items to cache
val fut = Future{
Thread.sleep(5000)
List(Item(s"hello $project"), Item(s"world $project"))
}
fut.map(LoadedItems(_)).pipeTo(self)
}
}
然后对其进行测试:
object ItemProviderTest extends App{
import ItemProvider._
val system = ActorSystem("test")
import system.dispatcher
val provider = system.actorOf(Props[ItemProviderRouter])
implicit val timeout = Timeout(10 seconds)
for(i <- 1 until 20){
val afut = provider ? GetItems("a")
val bfut = provider ? GetItems("b")
afut onSuccess{
case ItemResults(items) => println(s"got items list of $items for project a")
}
bfut onSuccess{
case ItemResults(items) => println(s"got items list of $items for project b")
}
}
}
对象项ProviderTest扩展应用程序{
导入项目提供程序_
val系统=ActorSystem(“测试”)
导入system.dispatcher
val provider=system.actorOf(Props[ItemProviderRouter])
隐式val超时=超时(10秒)
对于(i println(s“获得项目a的$items项目列表”)
}
B但是成功了吗{
案例ItemResults(items)=>println(s“获得项目b的$items列表”)
}
}
}
为了简单起见,我使用了一个实际的参与者来进行路由,而不是自定义路由器,但是如果性能(即邮箱命中),您也可以在这里实现一个自定义路由器对你来说很重要。我看起来它可以为他们服务,因为你正在使用
未来
,但这可能是多余的工作,例如,如果你在很短的时间内收到相同的请求。你需要一种方法来告诉后续的请求,你已经有了响应。所以,是的,你可能需要请求的映射对于未来,当一个新的请求出现时,请使用映射检查,如果该请求有未来
,请将未来
响应发送给该客户端。我认为您不能使用变成
。当您有一个状态机,并且您有多个状态机时,您需要变成
ne.每个单独的请求都有一个状态机,有两种状态:抓取和抓取。我对akka完全是初学者。但我不明白为什么我不能将客户端放在挂起列表上。当项目准备好后,Itemprovider可以向所有等待的客户端发送结果。在我看来,这是一个很好的选择啊,你也可以这样做。当然。@jack,我可以如果你不介意它不是java的话,就把你的问题代码化为一个Scala的解决方案。如果你想看的话,请告诉我。这看起来非常像我需要的。我明天试试。谢谢。把FETCHIKES项放在FITKIDEMESSLIDER的演员里是有意义的吗?杰克,当然,如果你想把负载分开。如果是这样的话,你可以在那里使用ask(?)
获得未来,然后像我在示例中所做的那样返回到self,或者使用tell(!)
直接等待响应,而不需要未来。