Non-Blocking File IO in Java
I want to write to a named pipe (already created) without blocking on the reader. My reader is another application that may go down. If the reader does go down, I want the writer a开发者_StackOverflow社区pplication to keep writing to that named pipe. Something like a this in Java
fopen(fPath, O_NONBLOCK)
So that when the reader comes up, it may resume from where it failed.
First I try to answer your questions. Next I will try to show you a code snippet I created that solves your problem using blocking IO.
Your questions
I want to write to a named pipe (already created) without blocking on the reader
You don't need non blocking IO to solve your problem. I think it can not even help you solve your problem. Blocking IO will also run good(maybe even better then non blocking IO because of the low concurrency). A plus is blocking IO is easier to program. Your reader can/should stay blocking.
My reader is another application that may go down. If the reader does go down, I want the writer application to neep writing to the named pipe. So that when the reader comes up, it may resume from where it failed.
just put the messages inside a blocking queue. Next write to the named pipe only when the reader is reading from it(happens automatically because of blocking IO). No need for non-blocking file IO when you use a blocking queue. The data is asynchronous delivered from the blocking queue when a reader is reading, which will sent your data from your writer to the reader.
Something like a fopen(fPath, O_NONBLOCK) in Java
You don't need non-blocking IO on the reader and even if you used it. just use blocking IO.
CODE SNIPPET
A created a little snippet which I believe demonstrates what your needs.
Components:
- Writer.java: reads lines from console as an example. When you start program enter text followed by enter which will sent it to your named pipe. The writer will resume writing if necessary.
- Reader.java: reads lines written from your named pipe(Writer.java).
- Named pipe: I assume you have created a pipe named "pipe" in the same directory.
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<StringBuffer> 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<StringBuffer>();
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;
}
}
}
}
If you want pipes to stay active and queue up messages, you probably want a messaging system rather than a raw pipe. In Java, the standard API is called "Java Messaging System" (JMS), and there are many standard implementations-- the most common of which I've seen being Apache ActiveMQ. If you want a cross-platform, sockets-like interface that does buffering and recovery I might suggest 0MQ, which while not being "pure Java" has bindings for many languages and excellent performance.
If there was such a thing as non-blocking file I/O in Java, which there isn't, a write to a named pipe that wasn't being read would return zero and not write anything. So non-blocking isn't part of the solution.
There's also the issue that named pipes have a finite buffer size. They aren't infinite queues regardless of whether there is a reading process or not. I agree with the suggestion to look into JMS.
You should be able to use NIO's asynch write
on a UNIX FIFO, just as you can to any other file:
AsynchronousFileChannel channel = AsynchronousFileChannel.open(...);
Future<Integer> writeFuture = channel.write(...);
... or...
channel.write(..., myCompletionHandler);
However, it's not clear to me what you want to happen when the FIFO isn't accepting writes. Do you want it to buffer? If so you'll need to provide it within the Java program. Do you want it to time out? There's no simple timeout option on Java file writes.
These aren't insurmountable problems. If you're determined you can probably get something working. But I wonder whether you'd not find life much easier if you just used a TCP socket or a JMS queue.
精彩评论