Java 推迟邮件的最佳方式是什么?

Java 推迟邮件的最佳方式是什么?,java,scala,akka,Java,Scala,Akka,我有一个actor“ItemProvider”,它可以接收“getItems”消息。 ItemProvider管理项目的项。因此,我可以有几个“getItems”消息请求项目A的项目,以及其他“getItems”消息请求项目B的项目 “itemProvider”第一次收到这样一条消息时,它需要调用一个服务来实际获取项目 (这可能需要一分钟,因为服务返回一个未来,所以它不会阻止参与者)。在此等待期间,其他“getItems”消息可能会到达 项目“ItemProvider”缓存从服务接收的“Item

我有一个actor“ItemProvider”,它可以接收“getItems”消息。 ItemProvider管理项目的项。因此,我可以有几个“getItems”消息请求项目A的项目,以及其他“getItems”消息请求项目B的项目

“itemProvider”第一次收到这样一条消息时,它需要调用一个服务来实际获取项目 (这可能需要一分钟,因为服务返回一个未来,所以它不会阻止参与者)。在此等待期间,其他“getItems”消息可能会到达

项目“ItemProvider”缓存从服务接收的“Items”。 因此,在1分钟的装载时间后,它可以立即为物品提供服务

我很确定“ItemProvider”应该使用Akka的Been功能。但是,它应该如何处理它不能立即服务的客户呢

我可以想到以下几种选择:

  • ItemProvider保存一个pendingMessages列表。并且它无法提供服务的消息将添加到此列表中。当ItemProvider“就绪”时,它将处理挂起的客户端

  • ItemProvider将消息发送回其父级。家长将重新发出该消息

  • ItemProvider使用调度程序。并在将来再次获得信息

  • 可能不使用Been,而是使用AbstractFSM类

    有人知道Akka实现ItemProvider的最佳方法吗


  • 客户端应按计划重新发送幂等请求,直到收到满意的答复或超时为止

    您是否需要更多的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(!)
    直接等待响应,而不需要未来。