【问题标题】:Fastest way to GZIP and UDP a large amount of Strings in Java在 Java 中使用 GZIP 和 UDP 处理大量字符串的最快方法
【发布时间】:2012-11-20 16:12:10
【问题描述】:

我正在实现一个日志系统,它需要使用 GZIP 对日志消息进行编码并通过 UDP 发送出去。

到目前为止我得到的是:

初始化:

DatagramSocket sock = new DatagramSocket(); 
baos = new ByteArrayOutputStream();
printStream = new PrintStream(new GZIPOutputStream(baos));

这个 printStream 然后从记录器中传递出去 - 消息将通过它到达

那么每次消息到达时:

byte[] d = baos.toByteArray();
DatagramPacket dp = new DatagramPacket(d,d.length,host,port);
sock.send(dp);

目前让我感到困惑的是,我找不到从 ByteArrayOutputStream 中删除数据的方法(toByteArray() 只需要一个副本),而且我担心每次重新创建所有三个流对象都会效率低下。

有没有办法从流中删除发送的数据?还是我应该完全转向另一个方向?

【问题讨论】:

  • 注意:如果字符串很短,GZIPed 会更大。

标签: java udp gzip bytearrayoutputstream


【解决方案1】:

您必须为每条消息创建一个新流;否则,每次调用toByteArray() 都会再次发送之前的所有消息。

更好的方法可能是用GZIPOutputStream 包装TCP 套接字的OutputStream

printStream = new PrintStream(new GZIPOutputStream(sock.getOutputStream()));

别忘了在每条消息后刷新PrintStream,否则什么都不会发生。

如果速度真的那么重要,您应该考虑使用DatagramChannel 而不是旧的(慢)Steam API。这应该可以帮助您开始:

ByteBuffer buffer = ByteBuffer.allocate( 1000 );
ByteBufferOutputStream bufferOutput = new ByteBufferOutputStream( buffer );
GZIPOutputStream output = new GZIPOutputStream( bufferOutput );
OutputStreamWriter writer = new OutputStreamWriter( output, "UTF-8" );
writer.write( "log message\n" );
writer.close();

sock.getChannel().open(); // do this once
sock.getChannel().write( buffer ); // Send compressed data

注意:您可以通过倒带重复使用buffer,但所有流必须为每条消息创建一次。

【讨论】:

    【解决方案2】:

    如果速度很重要,使用 GZIP 会有所帮助,这是值得检查的。 (这会增加一些延迟)

    public static void main(String... args) throws IOException {
        test("Hello World");
        test("Nov 20, 2012 4:55:11 PM Main main\n" +
                "INFO: Hello World log message");
    }
    
    private static void test(String s) throws IOException {
        byte[] bytes = s.getBytes("UTF-8");
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        GZIPOutputStream outputStream = new GZIPOutputStream(baos);
        outputStream.write(bytes);
        outputStream.close();
        byte[] bytes2 = baos.toByteArray();
        System.out.println("'" + s + "' raw.length=" + bytes.length + " gzip.length=" + bytes2.length);
    }
    

    打印

    'Hello World' raw.length=11 gzip.length=31
    'Nov 20, 2012 4:55:11 PM Main main
    INFO: Hello World log message' raw.length=63 gzip.length=80
    

    【讨论】:

    • GZIP 不是我的决定。我将日志消息发送到 Graylog,这会强制我使用 GZIP 和 UDP。
    • GZIP 的兼容性是合理的。如果你能找到一种方法来批量合并日志条目,这将大大提高效率。如果将来可以选择,BTW DeflatorOutputStream 的页眉/页脚要小得多。
    • 如果您调用toByteArray() 太多次,我会担心堆空间不足。
    • @heez 这只是一个问题。如果您保留数组。与压缩相比,创建短期对象相对便宜。
    【解决方案3】:

    答案对我的问题的其他方面很有帮助,但对于实际问题 - 有一种方法可以清除 ByteArrayOutputStream 中的数据。它有一个 reset() 方法。它实际上并没有清除缓冲区,而是将 count 属性重置为 0,导致它忽略缓冲区中已经存在的任何数据。

    请注意,在重置底层 ByteArrayOutputStream 后写入 GZIPOutputStream 会导致错误,所以我还没有找到重用所有内容的方法。

    【讨论】:

      猜你喜欢
      • 2011-05-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-15
      • 1970-01-01
      • 2017-08-24
      • 1970-01-01
      相关资源
      最近更新 更多