基于超时的BufferedWriter flush

时间:2015-06-01 21:48:28

标签: java java-io bufferedwriter

我正在使用默认大小为 8192 字符的BufferedWriter来将行写入本地文件。使用BufferedReader readLine 方法从套接字输入流读取行,阻止I / O.

平均线长 50 个字符。一切运行良好且足够快(每秒超过1百万行)但是如果客户端停止写入,则当前存储在BufferedWriter缓冲区中的行将不会刷新到磁盘。事实上,在客户端恢复写入或连接关闭之前,缓冲的字符不会刷新到磁盘。这转换为客户端传输时间线与此线路提交文件的时间之间的延迟,因此长尾延迟会增加。

有没有办法在超时时刷新不完整的BufferedWriter缓冲区,例如在100毫秒内?

4 个答案:

答案 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.时将其缩小,直到找到所需的性能和延迟之间的平衡为止。