我正在使用默认大小为 8192 字符的BufferedWriter
来将行写入本地文件。使用BufferedReader
readLine 方法从套接字输入流读取行,阻止I / O.
平均线长 50 个字符。一切运行良好且足够快(每秒超过1百万行)但是如果客户端停止写入,则当前存储在BufferedWriter
缓冲区中的行将不会刷新到磁盘。事实上,在客户端恢复写入或连接关闭之前,缓冲的字符不会刷新到磁盘。这转换为客户端传输时间线与此线路提交文件的时间之间的延迟,因此长尾延迟会增加。
有没有办法在超时时刷新不完整的BufferedWriter
缓冲区,例如在100毫秒内?
答案 0 :(得分:3)
这样的事情怎么样?它不是真实的 BufferedWriter
,但它是Writer
。它的工作原理是定期检查底层的最后一个编写器,希望是无缓冲的编写器,然后如果它超过超时则刷新BufferedWriter
。
public class PeriodicFlushingBufferedWriter extends Writer {
protected final MonitoredWriter monitoredWriter;
protected final BufferedWriter writer;
protected final long timeout;
protected final Thread thread;
public PeriodicFlushingBufferedWriter(Writer out, long timeout) {
this(out, 8192, timeout);
}
public PeriodicFlushingBufferedWriter(Writer out, int sz, final long timeout) {
monitoredWriter = new MonitoredWriter(out);
writer = new BufferedWriter(monitoredWriter, sz);
this.timeout = timeout;
thread = new Thread(new Runnable() {
@Override
public void run() {
long deadline = System.currentTimeMillis() + timeout;
while (!Thread.interrupted()) {
try {
Thread.sleep(Math.max(deadline - System.currentTimeMillis(), 0));
} catch (InterruptedException e) {
return;
}
synchronized (PeriodicFlushingBufferedWriter.this) {
if (Thread.interrupted()) {
return;
}
long lastWrite = monitoredWriter.getLastWrite();
if (System.currentTimeMillis() - lastWrite >= timeout) {
try {
writer.flush();
} catch (IOException e) {
}
}
deadline = lastWrite + timeout;
}
}
}
});
thread.start();
}
@Override
public synchronized void write(char[] cbuf, int off, int len) throws IOException {
this.writer.write(cbuf, off, len);
}
@Override
public synchronized void flush() throws IOException {
this.writer.flush();
}
@Override
public synchronized void close() throws IOException {
try {
thread.interrupt();
} finally {
this.writer.close();
}
}
private static class MonitoredWriter extends FilterWriter {
protected final AtomicLong lastWrite = new AtomicLong();
protected MonitoredWriter(Writer out) {
super(out);
}
@Override
public void write(int c) throws IOException {
lastWrite.set(System.currentTimeMillis());
super.write(c);
}
@Override
public void write(char[] cbuf, int off, int len) throws IOException {
lastWrite.set(System.currentTimeMillis());
super.write(cbuf, off, len);
}
@Override
public void write(String str, int off, int len) throws IOException {
lastWrite.set(System.currentTimeMillis());
super.write(str, off, len);
}
@Override
public void flush() throws IOException {
lastWrite.set(System.currentTimeMillis());
super.flush();
}
public long getLastWrite() {
return this.lastWrite.get();
}
}
}
答案 1 :(得分:0)
@copeg是对的 - 在每一行之后冲洗它。在一段时间内很容易冲洗它,但只有一半记录并且无法进行记录的意义是什么?
答案 2 :(得分:0)
您可以在此处应用Observer,Manager和Factory模式,并让中央BufferedWriterManager生成BufferedWriters并维护活动实例列表。内部线程可能会定期唤醒并刷新活动实例。这也可能是弱引用的机会,因此您的消费者无需明确释放该对象。相反,GC将完成工作,并且您的管理器只需在其内部引用变为空时(即,当删除所有强引用时)处理该情况。
答案 3 :(得分:0)
不要尝试这种复杂的方案,这太难了。只需减小缓冲区的大小,在构造BufferedWriter.
时将其缩小,直到找到所需的性能和延迟之间的平衡为止。