Apache Avro

Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 21:06, 30 января 2019.
Apache Avro
Avro-logo.png
Создатели: Apache
Постоянный выпуск: 1.8.2 / 20 мая 2017
Состояние разработки: активная
Написана на: Java,C, C++,C#,Python
Лицензия: Apache License 2.0
Веб-сайт avro.apache.org

Apache Avro — система сериализации данных, разработанная в рамках проекта Hadoop. Система использует JSON для определения структуры данных (схемы), которые сериализуются в компактный бинарный формат.

Сравнение с другими системами

Avro имеет схожий функционал с такими системами как Thrift, Protocol Buffers, и т.д. Но имеет следующие фундаментальные отличия от этих систем:

  • Динамическая типизация: Avro не нуждается в предгенерации кода для работы. Данные всегда сопровождаются схемой, которая может быть обработана без статических типов, генерации кода.
  • Так как схема всегда рядом с данными, то нет накладных расходов на информации о типах при сериализации данных.
  • Нет идентификаторов полей: Когда схема меняется, то хранится старая и новая схема, которая используются при обработке данных, все конфликты могут быть разрешены символьно ввиду отсутствия строгих целых идентификаторов.

Особенности:

  • Компактный и быстрый бинарный формат.
  • Удаленный вызов процедур (RPC).
  • Контейнер для хранения бинарных данных.
  • Простая интеграция с динамическими языками. Генерация кода для статически типизированных языков.

Схема

Схема является описанием структуры данных с помощью JSON. Схема допускает как простые типы — (null, boolean, int, long, float, double, bytes, string), так и сложные составные типы (словари, массивы, перечисления и другие)
Пример схемы:

{
    "id": 42,
    "name":  "Alexander",
    "metainfo": [
        {"name": "age", "value": "20"},
        {"name": "email", "value": null}
    ]
}


Контейнер

Контейнер состоит из:

  • Заголовок контейнера
  • Один или более блоков данных

Заголовок содержит:

  • Четыре байта, ASCII 'O', 'b', 'j', далее 1.
  • Метаданные файла, содержащие схему.
  • 16-байтное случайное число — маркер файла.

Для блоков данных Avro может использовать две кодировки: бинарные данные и в формате JSON. Эффективнее использовать бинарную кодировку, которая быстрее и компактнее, но для отладки удобнее использовать человекочитаемый формат JSON

Сообщение

Атрибуты сообщения:

  • doc, строка, описание сообщения
  • request, список именнованных, типизированных параметров схемы
  • response, имя схемы
  • error, список именнованных, типизированных параметров схемы (среди ошибок).
  • one-way boolean, определяет может ли response быть null

Протокол

Протокол описывается RPC интерфейс. Как и схема, протокол описывается через JSON и имеет следующие поля:

  • protocol, строка, имя протокола (обязательное поле)
  • namespace, строка, которая дополняет/уточняет имя протокола
  • doc, строка, описание протокола на естественном языка
  • types, список определения сложных типов (особый тип error определяет тип ошибки)
  • messages, JSON объект, где ключ — название сообщения, а значение — объект, чья структура описана выше.


Пример:

{
  "namespace": "com.acme",
  "protocol": "HelloWorld",
  "doc": "Protocol Greetings",

  "types": [
    {"name": "Greeting", "type": "record", "fields": [
      {"name": "message", "type": "string"}]},
    {"name": "Curse", "type": "error", "fields": [
      {"name": "message", "type": "string"}]}
  ],

  "messages": {
    "hello": {
      "doc": "Say hello.",
      "request": [{"name": "greeting", "type": "Greeting" }],
      "response": "Greeting",
      "errors": ["Curse"]
    }
  }
}

Avro IDL

В дополнение к JSON для определения схем и протоколов, Avro имеет экспериментальную поддержку альтернативного языка описания интерфейса (IDL), чей синтаксис больше похож на Java, C/C++ или Python

/**
 * An example protocol in Avro IDL
 */
@namespace("org.apache.avro.test")
protocol Simple {

  @aliases(["org.foo.KindOf"])
  enum Kind {
    FOO,
    BAR, // the bar enum value
    BAZ
  }

  fixed MD5(16);

  record TestRecord {
    @order("ignore")
    string name;

    @order("descending")
    Kind kind;

    MD5 hash;

    union { MD5, null} @aliases(["hash"]) nullableHash;

    array<long> arrayOfLongs;
  }

  error TestError {
    string message;
  }

  string hello(string greeting);
  TestRecord echo(TestRecord `record`);
  int add(int arg1, int arg2);
  bytes echoBytes(bytes data);
  void `error`() throws TestError;
  void ping() oneway;
}

Сериализация и десериализация

Данные в Avro могут храниться с соответствующей схемой, что означает, что сериализованный элемент может быть прочитан без предварительного знания схемы. Пример сериализации и десериализации кода в Python[Источник 1]. Сериализация:

import avro.schema
from avro.datafile import DataFileReader, DataFileWriter
from avro.io import DatumReader, DatumWriter

schema = avro.schema.parse(open("user.avsc").read())  # need to know the schema to write

writer = DataFileWriter(open("users.avro", "w"), DatumWriter(), schema)
writer.append({"name": "Alyssa", "favorite_number": 256})
writer.append({"name": "Ben", "favorite_number": 7, "favorite_color": "red"})
writer.close()

Файл "users.avro" будет содержать схему в формате JSON и компактное двоичное представление данных[Источник 2]:

$ od -c users.avro
0000000    O   b   j 001 004 026   a   v   r   o   .   s   c   h   e   m
0000020    a 272 003   {   "   t   y   p   e   "   :       "   r   e   c
0000040    o   r   d   "   ,       "   n   a   m   e   s   p   a   c   e
0000060    "   :       "   e   x   a   m   p   l   e   .   a   v   r   o
0000100    "   ,       "   n   a   m   e   "   :       "   U   s   e   r
0000120    "   ,       "   f   i   e   l   d   s   "   :       [   {   "
0000140    t   y   p   e   "   :       "   s   t   r   i   n   g   "   ,
0000160        "   n   a   m   e   "   :       "   n   a   m   e   "   }
0000200    ,       {   "   t   y   p   e   "   :       [   "   i   n   t
0000220    "   ,       "   n   u   l   l   "   ]   ,       "   n   a   m
0000240    e   "   :       "   f   a   v   o   r   i   t   e   _   n   u
0000260    m   b   e   r   "   }   ,       {   "   t   y   p   e   "   :
0000300        [   "   s   t   r   i   n   g   "   ,       "   n   u   l
0000320    l   "   ]   ,       "   n   a   m   e   "   :       "   f   a
0000340    v   o   r   i   t   e   _   c   o   l   o   r   "   }   ]   }
0000360  024   a   v   r   o   .   c   o   d   e   c  \b   n   u   l   l
0000400   \0 211 266   / 030 334   ˪  **   P 314 341 267 234 310   5 213
0000420    6 004   ,  \f   A   l   y   s   s   a  \0 200 004 002 006   B
0000440    e   n  \0 016  \0 006   r   e   d 211 266   / 030 334   ˪  **
0000460    P 314 341 267 234 310   5 213   6
0000471

Десериализация:

reader = DataFileReader(open("users.avro", "r"), DatumReader())  # no need to know the schema to read
for user in reader:
    print user
reader.close()

Это выводит:

{u'favorite_color': None, u'favorite_number': 256, u'name': u'Alyssa'}
{u'favorite_color': u'red', u'favorite_number': 7, u'name': u'Ben'}

API

Avro может использоваться в связке с любым языком, для следующих языков уже есть API:

Пример работы

Источники

  1. Docs // AvroApache. [2019]. Дата обновления:12.05.2018. URL:https://avro.apache.org/docs/current/gettingstartedpython.html (дата обращения: 30.12.2018)
  2. Docs // AvroApache. [2019]. Дата обновления:12.05.2018. URL:https://avro.apache.org/docs/current/spec.html#Data+Serialization (дата обращения: 30.12.2018)