当强制关闭时scala关闭钩子

时间:2014-10-11 11:54:09

标签: windows scala command-line

我的scala程序中有关闭钩子并且在使用ctrl + c退出时工作正常,但是在用户刚退出命令行窗口之后还有一些方法可以执行某些操作,因为不会以这种方式调用shutdown吗?

1 个答案:

答案 0 :(得分:1)

我很难想象你能保证资源得到清理,无论如何。您的申请终止的原因有很多:定期终止,例外终止,用户停止流程,机器死亡,......

但是,您可以构建各种安全网:正如您所做的那样,可以定义关闭处理程序,以便在应用程序停止时清理资源。现在,如果你想确保清理一些资源,即使整个过程停止运行,那么你可以创建第二个过程,其唯一目的是检查你的应用程序是否仍然在运行,如果它不是。 t,清理资源。

现在这需要两个进程至少进行通信:一个进程需要知道另一个进程是否仍在运行。

有多种沟通方式。根据您希望设计的复杂程度,有些更适合。我可以想到以下沟通方式:

  • 让一个进程构建您的应用程序。此过程将是另一个的父进程,可以检查另一个进程是否仍在运行

  • 让进程使用远程过程调用(RMI)进行通信。您可以使用方法ping定义一个返回pong的接口,允许一个进程检查另一个进程是否仍然存在。

  • 在比RMI更低的级别上使用套接字进行类似的通信。

  • 进程可以通过文件系统进行通信,即一个进程通过写入文件来ping另一个进程,如果另一个进程没有对协议作出反应,则进程清理资源。这可能不如前一种方法可靠,也可能更慢,尽管我没有数据来支持我的直觉。

我不是专家,所以肯定有很多选择。

您可以尝试这样做:

package application

import java.io._
import java.nio.file._
import java.nio.file.attribute.BasicFileAttributes

object Bootstrap {
  def main(args: Array[String]): Unit = {
    val workingDirectory = Files.createTempDirectory("newTemp")
    println("Working Directory: " + workingDirectory)
    val processBuilder = new ProcessBuilder("java", "-jar", "/path/to/process.jar", workingDirectory.toAbsolutePath.toString)
    processBuilder.directory(workingDirectory.toFile)
    val process = processBuilder.start()
    new StreamGobbler(process.getErrorStream, "ERROR").start()
    new StreamGobbler(process.getInputStream, "OUTPUT").start()
    process.waitFor()
    println("Process stopped working")
    cleanup(workingDirectory)
  }

  def cleanup(workingDirectory: Path): Unit = {
    Files.walkFileTree(workingDirectory, new SimpleFileVisitor[Path]{
      override def visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult = {
        Files.deleteIfExists(file)
        super.visitFile(file, attrs)
      }

      override def postVisitDirectory(dir: Path, exc: IOException): FileVisitResult = {
        Files.deleteIfExists(dir)
        super.postVisitDirectory(dir, exc)
      }
    })
  }
}

class StreamGobbler(is: InputStream, `type`: String) extends Thread {

  override def run(): Unit = {
    val isr = new InputStreamReader(is)
    val br = new BufferedReader(isr)
    var line = br.readLine()
    while (line != null){
      System.out.println(`type` + "> " + line)
      line = br.readLine()
    }
  }
}

object Process {
  def main(args: Array[String]): Unit = {
    val workingDirectory = Paths.get(args(0))
    Files.createTempFile(workingDirectory, "Resource", ".txt")
    for(i <- 1 to 8) {
      println("Process is running")
      Thread.sleep(1000)
    }
    println("Suppose the process got killed here")
  }
}

要运行此操作,请创建一个以Process为入口点的可执行jar,更新"path/to/process.jar"并运行Bootstrap。即使您终止调用的子进程,仍应清除资源。

我从ProcessBuilder: Forwarding stdout and stderr of started processes without blocking the main thread获取StreamGobbler,只将其从Java翻译为Scala。

请注意,如果您的Bootstrap进程被终止,那么您的资源将无法清理。根据清理工作的重要性,您可以

  • 创建多个独立的清理进程,在没有master的情况下运行(即,让父进程只创建多个清理进程,通信(例如,通过RMI)以独立地协调清除父进程是否仍在运行)

  • 在不同的机器上创建多个独立的清理过程,这些过程通过网络进行通信。即使有当地的电力愤怒,这也可以让你清理。

请注意,为了最大限度地降低应用程序未正确清理的风险,您的清理工作将变得更加复杂。可能还有现有的解决方案(也许应用服务器会为你做这样的事情吗?),但现有的解决方案可能会强制依赖你不需要的东西。