Java 如何利用akka remote和steam实现容错文件上传

Java 如何利用akka remote和steam实现容错文件上传,java,akka,akka-stream,Java,Akka,Akka Stream,我是阿卡族的初学者。我正在使用Java 我正在用Akka做一个文件传输系统 目前,我已完成发送Actor1Local->Actor2Remote文件 现在, 当我在传输文件时遇到问题,我会考虑如何解决。 然后我有一个问题。问题如下 如果在传输文件时丢失了网络连接,文件传输将失败90%。 我将在几分钟后恢复网络连接 是否可以传输其余的文件数据?剩余10% 如果可能的话,请给我一些建议 这是我的简单代码。 谢谢: Actor1本地 private Behavior<Event> onTi

我是阿卡族的初学者。我正在使用Java

我正在用Akka做一个文件传输系统

目前,我已完成发送Actor1Local->Actor2Remote文件

现在,

当我在传输文件时遇到问题,我会考虑如何解决。 然后我有一个问题。问题如下

如果在传输文件时丢失了网络连接,文件传输将失败90%。 我将在几分钟后恢复网络连接

是否可以传输其余的文件数据?剩余10%

如果可能的话,请给我一些建议

这是我的简单代码。 谢谢:

Actor1本地

private Behavior<Event> onTick() {
    ....
    String fileName = "test.zip";
    Source<ByteString, CompletionStage<IOResult>> logs = FileIO.fromPath(Paths.get(fileName));
    logs.runForeach(f -> originalSize += f.size(), mat).thenRun(() -> System.out.println("originalSize : " + originalSize));
    SourceRef<ByteString> logsRef = logs.runWith(StreamRefs.sourceRef(), mat);
    getContext().ask(
            Receiver.FileTransfered.class,
            selectedReceiver,
            timeout,
            responseRef -> new Receiver.TransferFile(logsRef, responseRef, fileName),
            (response, failure) -> {
                if (response != null) {
                    return new TransferCompleted(fileName, response.transferedSize);
                } else {
                    return new JobFailed("Processing timed out", fileName);
                }
            }
    );
}
Actor2遥控器

public static Behavior<Command> create() {
    return Behaviors.setup(context -> {
        ...
        Materializer mat = Materializer.createMaterializer(context);
        return Behaviors.receive(Command.class)
                .onMessage(TransferFile.class, command -> {
                    command.sourceRef.getSource().runWith(FileIO.toPath(Paths.get("test.zip")), mat);
                    command.replyTo.tell(new FileTransfered("filename", 1024));
                    return Behaviors.same();
                }).build();
    });
}

要正确实现具有容错功能的文件传输,您需要考虑以下几点:

如何确定必须为给定文件恢复传输。 如何找到恢复传输的起点。 下面的实现对1和2进行了非常简单的假设

文件名是唯一的,因此可用于此类标识。严格地说,这是不正确的,例如,您可以从不同的文件夹传输具有相同名称的文件。或者来自不同的节点,等等。您必须根据您的用例重新调整它。 假定接收器端的最后/所有写入操作正确写入了所有字节,写入的字节总数指示恢复传输的点。如果无法保证这一点,则需要从逻辑上将原始文件拆分为块,并将每个块的哈希值、大小和位置传输给接收方,接收方必须验证其一侧的块,并找到正确的指针以恢复传输。 这比2多一点:这个实现忽略了传输问题的识别,而是关注1和2。 守则:

对象发送器{ 密封特征命令 案例类上载文件:字符串扩展命令 case类StartWithIndexfile:String,index:Long扩展Sender.Command def behaviorreceiver:ActorRef[Receiver.Command]:行为[Sender.Command]=行为.setup[Sender.Command]{ctx=> 隐式val materializer:materializer=SystemMaterializerCx.system.materializer 行为。接收消息{ 案例上传文件=> receiver.tellReceiver.InitUploadfile,ctx.self.窄带[StartWithIndex] ctx.log.infosInitiating$file的上载 行为是一样的 案例StarTwithinDeskFile,starWith=> val source=FileIO.fromPathPaths.getfile,chunkSize=8192,starWith val ref=source.runWithStreamRefs.sourceRef ctx.log.infoss开始上载$file receiver.tellReceiver.Uploadfile,参考 行为是一样的 } } } 目标接收器{ 密封特征命令 case类InitUploadfile:String,replyTo:ActorRef[Sender.StartWithIndex]扩展命令 case类Uploadfile:String,fileSource:SourceRef[ByteString]extends命令 val行为:行为[Receiver.Command]=行为。设置[Receiver.Command]{ctx=> 隐式val materializer:materializer=SystemMaterializerCx.system.materializer 行为。接收消息{ case InitUploadpath,replyTo=> val file=fileAtDestinationpath val index=如果file.exists file.length否则0 指针$index处$file的ctx.log.infosGot init命令 replyTo.tellssender.startwithindepath,index.toLong 行为是一样的 案例上传路径,文件源=> val file=fileAtDestinationpath val sink=如果文件.1存在{ FileIO.toPathfile.toPath,SetStandardOpenOption.APPEND,StandardOpenOption.WRITE }否则{ FileIO.toPathfile.toPath,SetStandardOpenOption.CREATE\u NEW,StandardOpenOption.WRITE } ctx.log.infosSaving文件到${file.toPath} fileSource.runWithLink 行为是一样的 } } } 一些辅助方法

val destination: File = Files.createTempDirectory("destination").toFile

def fileAtDestination(file: String) = {
  val name = new File(file).getName
  new File(destination, name)
}

def writeRandomToFile(file: File, size: Int): Unit = {
  val out = new FileOutputStream(file, true)
  (0 until size).foreach { _ =>
    out.write(Random.nextPrintableChar())
  }
  out.close()
}

最后是一些测试代码

// sender and receiver bootstrapping is omitted

//Create some dummy file to upload
val file: Path = Files.createTempFile("test", "test")
writeRandomToFile(file.toFile, 1000)

//Initiate a new upload
sender.tell(Sender.Upload(file.toAbsolutePath.toString))
// Sleep to allow file upload to finish
Thread.sleep(1000)

//Write more data to the file to emulate a failure
writeRandomToFile(file.toFile, 1000)
//Initiate a new upload that will "recover" from the previous upload 
sender.tell(Sender.Upload(file.toAbsolutePath.toString))

最后,整个过程可以定义为


要正确实现具有容错功能的文件传输,您需要考虑以下几点:

如何确定必须为给定文件恢复传输。 如何找到恢复传输的起点。 下面的实现对1和2进行了非常简单的假设

文件名是唯一的,因此可用于此类标识。严格地说,这是不正确的,例如,您可以从不同的文件夹传输具有相同名称的文件。或者来自不同的节点,等等。您必须根据您的用例重新调整它。 假定接收器端的最后/所有写入操作正确写入了所有字节,写入的字节总数指示恢复传输的点。如果不能保证这一点,则需要从逻辑上将原始文件分割为块,并将每个块的哈希值、大小和位置传输给接收方,接收方必须 在块的一侧进行idate,并找到恢复传输的正确指针。 这比2多一点:这个实现忽略了传输问题的识别,而是关注1和2。 守则:

对象发送器{ 密封特征命令 案例类上载文件:字符串扩展命令 case类StartWithIndexfile:String,index:Long扩展Sender.Command def behaviorreceiver:ActorRef[Receiver.Command]:行为[Sender.Command]=行为.setup[Sender.Command]{ctx=> 隐式val materializer:materializer=SystemMaterializerCx.system.materializer 行为。接收消息{ 案例上传文件=> receiver.tellReceiver.InitUploadfile,ctx.self.窄带[StartWithIndex] ctx.log.infosInitiating$file的上载 行为是一样的 案例StarTwithinDeskFile,starWith=> val source=FileIO.fromPathPaths.getfile,chunkSize=8192,starWith val ref=source.runWithStreamRefs.sourceRef ctx.log.infoss开始上载$file receiver.tellReceiver.Uploadfile,参考 行为是一样的 } } } 目标接收器{ 密封特征命令 case类InitUploadfile:String,replyTo:ActorRef[Sender.StartWithIndex]扩展命令 case类Uploadfile:String,fileSource:SourceRef[ByteString]extends命令 val行为:行为[Receiver.Command]=行为。设置[Receiver.Command]{ctx=> 隐式val materializer:materializer=SystemMaterializerCx.system.materializer 行为。接收消息{ case InitUploadpath,replyTo=> val file=fileAtDestinationpath val index=如果file.exists file.length否则0 指针$index处$file的ctx.log.infosGot init命令 replyTo.tellssender.startwithindepath,index.toLong 行为是一样的 案例上传路径,文件源=> val file=fileAtDestinationpath val sink=如果文件.1存在{ FileIO.toPathfile.toPath,SetStandardOpenOption.APPEND,StandardOpenOption.WRITE }否则{ FileIO.toPathfile.toPath,SetStandardOpenOption.CREATE\u NEW,StandardOpenOption.WRITE } ctx.log.infosSaving文件到${file.toPath} fileSource.runWithLink 行为是一样的 } } } 一些辅助方法

val destination: File = Files.createTempDirectory("destination").toFile

def fileAtDestination(file: String) = {
  val name = new File(file).getName
  new File(destination, name)
}

def writeRandomToFile(file: File, size: Int): Unit = {
  val out = new FileOutputStream(file, true)
  (0 until size).foreach { _ =>
    out.write(Random.nextPrintableChar())
  }
  out.close()
}

最后是一些测试代码

// sender and receiver bootstrapping is omitted

//Create some dummy file to upload
val file: Path = Files.createTempFile("test", "test")
writeRandomToFile(file.toFile, 1000)

//Initiate a new upload
sender.tell(Sender.Upload(file.toAbsolutePath.toString))
// Sleep to allow file upload to finish
Thread.sleep(1000)

//Write more data to the file to emulate a failure
writeRandomToFile(file.toFile, 1000)
//Initiate a new upload that will "recover" from the previous upload 
sender.tell(Sender.Upload(file.toAbsolutePath.toString))

最后,整个过程可以定义为


你对scala的解决方案感兴趣吗?@IvanStanislavciuc如果你给我这个解决方案,我会尽可能多地参考它:你对scala的解决方案感兴趣吗?@IvanStanislavciuc如果你给我这个解决方案,我会尽可能多地参考它:你的解决方案对我帮助很大。你向我解释说,尽管有不同的编程语言,但我能理解它们。我通过添加简单代码解决了这个问题,但这对我来说是个大问题。^;最后,我解决了这个问题。谢谢。@JaeilCho很高兴听到这个消息!我可以再问你一个问题吗?是否有方法在传输过程中停止并恢复文件?我尝试了很多东西,但都不管用。请求帮助你的解决方案对我帮助很大。你向我解释说,尽管有不同的编程语言,但我能理解它们。我通过添加简单代码解决了这个问题,但这对我来说是个大问题。^;最后,我解决了这个问题。谢谢。@JaeilCho很高兴听到这个消息!我可以再问你一个问题吗?是否有方法在传输过程中停止并恢复文件?我尝试了很多东西,但都不管用。请求帮助