Java中的非阻塞文件IO

我想写一个命名管道(已经创建)而不会阻塞读取器。 我的读者是另一个可能会失败的应用程序。 如果读者确实失败了,我希望编写器应用程序继续写入该命名管道。 像Java这样的东西

fopen(fPath, O_NONBLOCK) 

因此,当读者出现时,它可能会从失败的地方恢复。

首先,我试着回答你的问题。 接下来,我将尝试向您展示我创建的代码片段,使用阻止IO解决您的问题。

你的问题

我想写一个命名管道(已经创建)而不会阻塞读取器

您不需要非阻塞IO来解决您的问题。 我认为它甚至无法帮助您解决问题。 阻止IO也将运行良好(可能甚至比非阻塞IO更好,因为并发性低)。 一个加号是阻止IO更容易编程。 您的读者可以/应该保持阻止。

我的读者是另一个可能会失败的应用程序。 如果读者确实关闭,我希望编写器应用程序能够写入命名管道。 因此,当读者出现时,它可能会从失败的地方恢复。

只需将消息放入阻塞队列即可。 接下来只有在读取器读取时才写入命名管道(由于阻塞IO而自动发生)。 使用阻塞队列时不需要非阻塞文件IO。 当读者正在阅读时,数据是从阻塞队列异步传递的,这会将您的数据从您的编写器发送到阅读器。

类似于Java中的fopen(fPath,O_NONBLOCK)

您不需要在阅读器上使用非阻塞IO,即使您使用它也是如此。 只是使用阻塞IO。

代码链接

创建了一个小片段,我相信它可以展示您的需求。

组件:

  • Writer.java :从控制台读取行作为示例。 当您启动程序时,输入文本,然后输入,将其发送到您的命名管道。 如有必要,作者将继续写作。
  • Reader.java :读取从命名管道(Writer.java)写入的行。
  • 命名管道 :我假设您在同一目录中创建了一个名为“pipe”的管道。

Writer.java

 import java.io.BufferedWriter; import java.io.Console; import java.io.FileWriter; import java.io.PrintWriter; import java.util.concurrent.BlockingDeque; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingDeque; import java.util.logging.Level; import java.util.logging.Logger; public class Writer { private final BlockingDeque queue; private final String filename; public static void main(String[] args) throws Exception { final Console console = System.console(); final Writer writer = new Writer("pipe"); writer.init(); while(true) { String readLine = console.readLine(); writer.write(new StringBuffer(readLine)); } } public Writer(final String filename){ this.queue = new LinkedBlockingDeque(); this.filename = filename; } public void write(StringBuffer buf) { queue.add(buf); } public void init() { ExecutorService single = Executors.newSingleThreadExecutor(); Runnable runnable = new Runnable() { public void run() { while(true) { PrintWriter w = null; try { String toString = queue.take().toString(); w = new PrintWriter(new BufferedWriter(new FileWriter(filename)), true); w.println(toString); } catch (Exception ex) { Logger.getLogger(Writer.class.getName()).log(Level.SEVERE, null, ex); } } } }; single.submit(runnable); } } 

Reader.java

 import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; public class Reader { private final BufferedReader br; public Reader(final String filename) throws FileNotFoundException { br = new BufferedReader(new FileReader(filename)); } public String readLine() throws IOException { return br.readLine(); } public void close() { try { br.close(); } catch (IOException ex) { Logger.getLogger(Reader.class.getName()).log(Level.SEVERE, null, ex); } } public static void main(String[] args) throws FileNotFoundException { Reader reader = new Reader("pipe"); while(true) { try { String readLine = reader.readLine(); System.out.println("readLine = " + readLine); } catch (IOException ex) { reader.close(); break; } } } } 

如果您希望管道保持活动状态并排队消息,则可能需要消息传递系统而不是原始管道。 在Java中,标准API称为“Java消息系统”( JMS ),并且有许多标准实现 – 我最常见的是Apache ActiveMQ 。 如果你想要一个可以缓冲和恢复的跨平台,类似套接字的接口,我可能会建议0MQ ,虽然不是“纯Java”,但它具有许多语言的绑定和出色的性能。

如果在Java中存在非阻塞文件I / O这样的事情,那么对未被读取的命名管道的写入将返回零并且不写入任何内容。 因此,非阻塞不是解决方案的一部分。

还存在命名管道具有有限缓冲区大小的问题。 无论是否有阅读过程,它们都不是无限的队列。 我同意调查JMS的建议。

您应该能够在UNIX FIFO上使用NIO的异步write ,就像对任何其他文件一样:

  AsynchronousFileChannel channel = AsynchronousFileChannel.open(...); Future writeFuture = channel.write(...); 

… 要么…

  channel.write(..., myCompletionHandler); 

但是,当FIFO不接受写入时,我不清楚你想要发生什么。 你想要缓冲吗? 如果是这样,您将需要在Java程序中提供它。 你想让它超时吗? Java文件写入没有简单的超时选项。

这些不是不可克服的问题。 如果你确定你可能会得到一些有用的东西。 但我想知道如果你只使用TCP套接字或JMS队列,你是否会发现生活更容易。