Статьи

Подключение YAML к файлу или сети с помощью Chronicle Wire

обзор

Chronicle Wire предназначен для получения преимуществ текстового протокола с точки зрения простоты разработки и более быстрой отладки, но может переключаться на более производительный и меньший двоичный протокол без необходимости изменения кода.

В этой статье я рассмотрел производительность, которую вы могли бы достичь, используя текстовый формат на основе YAML для сериализации, десериализации. Одной из приятных особенностей является то, что вы можете использовать YAML для тестирования, отладки, но переключитесь на двоичный YAML, где это имеет смысл.

Можно даже смешивать и сочетать в одном потоке. например, используйте YAML для рукопожатия и переключитесь на двоичный YAML или даже какой-либо другой формат, когда вы определите, что оба конца совместимы.

Как выглядит код?

Для начала нужно иметь буфер для записи. Это может быть byte [], ByteBuffer, память вне кучи или даже адрес и длина, полученные из другой библиотеки.

1
2
// Bytes which wraps a ByteBuffer which is resized as needed.
Bytes<ByteBuffer> bytes = Bytes.elasticByteBuffer();

Теперь вы можете выбрать, какой формат вы используете. Поскольку форматы проводов сами по себе не буферизованы, вы можете использовать их с одним и тем же буфером, но в целом использование одного формата проводов проще.

1
2
3
4
5
6
7
Wire wire = new TextWire(bytes);
// or
Bytes<ByteBuffer> bytes2 = Bytes.elasticByteBuffer();
Wire wire2 = new BinaryWire(bytes2);
// or
Bytes<ByteBuffer> bytes3 = Bytes.elasticByteBuffer();
Wire wire3 = new RawWire(bytes3);

Существует множество опций, например, хотите ли вы числовые поля, такие как protobuf или SBE, или вы хотите использовать переменные (наименьшие) или фиксированные длины (самые быстрые) значения данных.

Для записи объекта вы можете сделать его Marshallable и определить readmarshallable и writeMarshallable, но сейчас давайте просто напишем некоторые данные.
С TextWire это печатает:

1
2
3
4
5
wire.write(() -> "message").text("Hello World")
      .write(() -> "number").int64(1234567890L)
       .write(() -> "code").asEnum(TimeUnit.SECONDS)
      .write(() -> "price").float64(10.50);
System.out.println(bytes);
1
2
3
4
// to obtain the underlying ByteBuffer to write to a Channel
ByteBuffer byteBuffer = bytes2.underlyingObject();
byteBuffer.position(0);
byteBuffer.limit(bytes2.length());

Однако вместо этого используйте BinaryWire, и он пишет (при печати в шестнадцатеричном формате):

1
2
3
4
message: Hello World
number: 1234567890
code: SECONDS
price: 10.5

Однако вместо этого используйте BinaryWire, и он пишет (при печати в шестнадцатеричном формате):

1
2
3
4
00000000 C7 6D 65 73 73 61 67 65  EB 48 65 6C 6C 6F 20 57 ·message ·Hello W
00000010 6F 72 6C 64 C6 6E 75 6D  62 65 72 A3 D2 02 96 49 orld·num ber····I
00000020 C4 63 6F 64 65 E7 53 45  43 4F 4E 44 53 C5 70 72 ·code·SE CONDS·pr
00000030 69 63 65 90 00 00 28 41                          ice···(A

Использование BinaryWire может быть вдвое меньше и вдвое быстрее, однако у нас есть возможность использовать RawWire, который отбрасывает все метаданные и может быть в 8 раз быстрее, чем плановый текст.

Вывод

Хотя двоичные протоколы могут быть быстрее, с ними гораздо сложнее работать. Если вы можете использовать текстовый формат, вы можете разрабатывать и отлаживать его намного быстрее, но если у вас есть возможность динамического переключения, вы можете выбрать производительность, когда вам это нужно.

Несмотря на то, что TextWire занимает менее 5 микросекунд, 99,99% времени на запись считывается объектом из 6 полей, но этого может быть достаточно быстро для многих приложений.

Что полезно, так это возможность идти быстрее, если вам нужно. RawWire был менее 600 наносекунд в 99,99% случаев без необходимости изменения кода.

Если требуется максимальная скорость, у нас есть интерфейс BytesMarshallable с урезанным API, который занимает менее 220 наносекунд в 99,99% времени.