在Java中GZIP和UDP大量字符串的最快方法



我正在实现一个日志记录系统,该系统需要使用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() 只需要一个副本),而且我担心每次重新创建所有三个流对象会效率低下。

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

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

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

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

另外,不要忘记在每条消息后刷新PrintStream,否则什么都不会发生。

如果速度真的那么重要,您应该考虑使用DatagramChannel而不是旧的(慢速)蒸汽 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 messagen" );
writer.close();
sock.getChannel().open(); // do this once
sock.getChannel().write( buffer ); // Send compressed data

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

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

public static void main(String... args) throws IOException {
    test("Hello World");
    test("Nov 20, 2012 4:55:11 PM Main mainn" +
            "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

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

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

最新更新