3

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

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

4

3 回答 3

1

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

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

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

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

如果速度真的那么重要,您应该考虑使用 aDatagramChannel而不是旧的(慢)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它,但所有流必须为每条消息创建一次。

于 2012-11-20T16:16:43.300 回答
1

值得检查的是,如果速度很重要,使用 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
于 2012-11-20T16:55:26.087 回答
0

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

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

于 2012-11-21T13:14:06.110 回答