Самый быстрый способ GZIP и UDP большое количество строк в Java


Я внедряю систему ведения журнала, которая должна кодировать сообщения журнала С помощью GZIP и отправлять их по UDP.

Пока что у меня есть:

Инициализация:

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

Этот поток печати затем передается из регистратора-сообщения будут поступать через него

Затем каждый раз, когда приходит сообщение:

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

Что меня сейчас ставит в тупик, так это то, что я не могу найти способ удалить данные из ByteArrayOutputStream (toByteArray () только берет копию), и я боюсь что воссоздание всех трех объектов потока каждый раз будет неэффективным.

Есть ли способ удалить отправленные данные из потока? Или мне следует смотреть совсем в другую сторону?

3 3

3 ответа:

Вы должны создать новый поток для каждого сообщения; в противном случае каждый вызов toByteArray() будет отправлять все предыдущие сообщения снова.

Лучший подход, вероятно, заключается в том, чтобы обернуть OutputStream сокета TCP с GZIPOutputStream:

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

Также не забывайте смывать PrintStream после каждого сообщения, иначе ничего не произойдет.

Если скорость действительно так важна, вы должны рассмотреть возможность использования DatagramChannel вместо старого (медленного) API steam. Это должно помочь вам начать:

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, перематывая его, но все потоки должны быть созданы один раз на сообщение.

Стоит проверить, что использование 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

Ответы были полезны с другими аспектами моей проблемы, но для фактического вопроса-есть способ очистить данные из потока ByteArrayOutputStream. Он имеет метод reset (). Он фактически не очищает буфер, но сбрасывает свойство count в 0, заставляя его игнорировать любые данные, уже находящиеся в буфере.

Обратите внимание, что запись в GZIPOutputStream после сброса базового ByteArrayOutputStream вызовет ошибку, поэтому я еще не нашел способ повторно использовать все.