知道akka演员何时完成

时间:2013-07-12 02:16:47

标签: java akka

有一些人和我一起工作,一直试图找出处理这个问题的最佳方法。这似乎应该是一个经常需要的标准事物,但由于某种原因,我们似乎无法得到正确的答案。

如果我有一些工作需要完成并且我在路由器上抛出一堆消息,我怎么知道所有的工作何时完成?例如,如果我们正在读取100万行文件的行并将行发送给actor来处理它,并且你需要处理下一个文件,但是必须等待第一个文件完成,你怎么知道什么时候它完了吗?

进一步评论。我知道并且使用了与Patters.ask()一起使用的Await.result()和Await.ready()。一个区别是,每条线都有一个未来,我们将有一个巨大的这些期货数组等待,而不仅仅是一个。另外,我们正在填充一个占用大量内存的大型域模型,并且不希望添加额外的内存来保存等待编写的内存中相同数量的未来,同时使用每个人在完成工作之后完成的工作没有等待内存等待要成文。

我们使用的是Java而不是Scala。

伪代码:

for(File file : files) {
    ...
    while((String line = getNextLine(fileStream)) != null) {
        router.tell(line, this.getSelf());
    }
    // we need to wait for this work to finish to do the next
    // file because it's dependent on the previous work
}

看起来你经常想做很多工作,并且知道演员什么时候结束。

2 个答案:

答案 0 :(得分:4)

我相信我有一个解决方案,它不涉及累积一大堆Future。首先,高层次的概念。将有两名参与者参与此流程。首先我们打电话给FilesProcessor。这个演员将是短暂的和有状态的。每当您想要按顺序处理一堆文件时,您就会启动此actor的实例并向其传递一条消息,其中包含您要处理的文件的名称(或路径)。当它完成所有文件的处理后,它会自行停止。第二个演员我们将调用LineProcessor。这个演员是无国籍的,长寿,汇集在路由器后面。它处理文件行,然后回复请求行处理的任何人,告诉他们已完成处理该行。现在进入代码。

首先是消息:

public class Messages {

  public static class ProcessFiles{
    public final List<String> fileNames;
    public ProcessFiles(List<String> fileNames){
      this.fileNames = fileNames;
    }
  }

  public static class ProcessLine{
    public final String line;
    public ProcessLine(String line){
      this.line = line;
    }
  }

  public static class LineProcessed{}

  public static LineProcessed LINE_PROCESSED = new LineProcessed();
}

FilesProcessor

public class FilesProcessor extends UntypedActor{
  private List<String> files;
  private int awaitingCount;
  private ActorRef router;

  @Override
  public void onReceive(Object msg) throws Exception {
    if (msg instanceof ProcessFiles){      
      ProcessFiles pf = (ProcessFiles)msg;
      router = ... //lookup router;
      files = pf.fileNames;
      processNextFile();
    }
    else if (msg instanceof LineProcessed){
      awaitingCount--;
      if (awaitingCount <= 0){
        processNextFile();
      }
    }

  }

  private void processNextFile(){
    if (files.isEmpty()) getContext().stop(getSelf());
    else{            
      String file = files.remove(0);
      BufferedReader in = openFile(file);
      String input = null;
      awaitingCount = 0;

      try{
        while((input = in.readLine()) != null){
          router.tell(new Messages.ProcessLine(input), getSelf());
          awaitingCount++;
        }        
      }
      catch(IOException e){
        e.printStackTrace();
        getContext().stop(getSelf());
      }

    }
  }

  private BufferedReader openFile(String name){
    //do whetever to load file 
    ...
  }

}

LineProcessor

public class LineProcessor extends UntypedActor{

  @Override
  public void onReceive(Object msg) throws Exception {
    if (msg instanceof ProcessLine){
      ProcessLine pl = (ProcessLine)msg;

      //Do whatever line processing...

      getSender().tell(Messages.LINE_PROCESSED, getSelf());
    }
  }

}

现在,线路处理器正在发送回复而没有其他内容。如果你需要根据线路的处理发回一些东西,你当然可以改变它。我确信这段代码不是防弹,我只想向您展示一个高级概念,说明如何在没有请求/响应语义和Future的情况下完成此流程。

如果您对此方法有任何疑问或想了解更多细节,请告诉我,我很乐意提供。

答案 1 :(得分:0)

在路由上使用context.setRecieveTimeout将邮件发送回发件人,并处理已处理的邮件数。当处理的邮件总数==发送的金额完成后。

如果你的路线要保持足够的忙碌状态setReceiveTimeout不会频繁发射,那么请安排自己的信息发回计数。