Как стать автором
Обновить

External Term Format

Время на прочтение3 мин
Количество просмотров2.8K
Если какой-либо программе необходимо передать данные эрланговсому серверу, эти данные предварительно должны быть сериализованы. То есть преобразованы в бинарный вид, так, чтобы эрланг мог потом распаковать их обратно. Обычно это делают с помощью ASN.1, google protobuf, thrift и т.д. Все это без сомнения достойные продукты.

В качестве альтернативного варианта можно рассмотреть использование эрланговского external term format. В исполняемой системе эрланга есть две функции - term_to_binary() и binary_to_term(), которые эффективно и быстро могут запаковать/распаковать любые значения в этот формат, а сам формат хорошо описан в документации — www.erlang.org/doc/apps/erts/erl_ext_dist.html

Как это все работает.


Ext. format по своей структуре очень прост. Обычно данные в нем имеют вид «тэг, данные» или «тэг, длина, данные». Тэг описывает какого типа данные запакованы.

Для основных типов данных тэги такие

  • Tuple — 104, кол-во элементов(1 байт), все элементы
  • List — 108, кол-во элементов(4 байта), все элементы, 106
  • Атом — 115, длина(1 байт), текст атома
  • Целое — 98, значение (4 байта), или для короткого целого (меньше 255) — 97, значение(1 байт)
  • Строка — 107, длина(2 байта), текст. В принципе строка может быть закодирована как список целых, но если надо передать короткую строку с 8битовым текстом, этот тэг — то что доктор прописал


Перед всеми запакованными данными должен стоять тэг 131. Это номер версии текущего ext. term format.

То есть эрланговское значение [{banknote, 100, rub}] упакуется в стркутуру:



В качестве proof of concept напишем, например, на питоне простую процедуру, которая запаковывет структуры питона в ext term format, и распакуем результат на эрланге…

Будем упаковывать питоновский список в эрланговский список, питоновский tuple в эрланговский tuple, целые числа, питоновские строки. Для атомов… ну… например создадим наследника от строки в питоне, чтобы их легко можно было отличить в процедуре упаковки от обычных строк.

Получается что-то типа:

from types import IntType, StringType, TupleType, ListType
from struct import pack
import socket

class atom(str): pass

def _eterm(x,accum):
    if type(x) is IntType:
        accum.append(pack('>Bi',98,x))
        return
    if type(x) is StringType:
        accum.append(pack(">BH",107,len(x)))
        accum.append(x)
        return
    if type(x) is TupleType:
        accum.append(pack("BB",104,len(x)))
        for term in x: _eterm(term,accum)
        return
    if type(x) is ListType:
        accum.append(pack(">BI",108,len(x)))
        for term in x:_eterm(term,accum)
        accum.append(chr(106))
        return
    if isinstance(x,atom):
        accum.append(pack("BB",115,len(x)))
        accum.append(x)
        return
    
    raise AssertionError("Cannot convert that type to erlang term %s"%(x))

def binary(X):
    accum = [chr(131)]
    _eterm(X,accum)
    return "".join(accum)    


Отлично. Теперь упакуем какую нибудь сложную структуру и передадим ее в эрланг самым простым способом.

pterm = (atom("vcard"),[(atom("firstname"),"Odobenus"),
                          (atom("lastname"),"Rosmarus"),
                          (atom("age"),48),
                          (atom("children"),[
                                             ("Dimon",1988),
                                             ("Natashka",1990),
                                             ("Katka",2000),
                                             ("Anka",2003)] )

erlterm = binary(pterm)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(erlterm,("localhost",10000))


То есть упаковали и послали udp пакет. В эрланге принимаем и расшифровываем:

1> gen_udp:open(10000,[binary]).
{ok,#Port<0.585>}
2> R=receive {udp,_ ,_,_,Bin} -> Bin end.
<<131,104,2,115,5,118,99,97,114,100,108,0,0,0,4,104,2,115,
  9,102,105,114,115,116,110,97,109,101,107,...>>
3> binary_to_term( R ).
{vcard,[{firstname,"Odobenus"},
        {lastname,"Rosmarus"},
        {age,48},
        {children,[{"Dimon",1988},
                   {"Natashka",1990},
                   {"Katka",2000},
                   {"Anka",2003}]}]}


Выводы


Мы передали (малой кровью) сложную структуру из питона в эрланг. Достоинства такого подходы —
  • Гибкость. Можно запаковать все что угодно.
  • Производительность. На эрланге binary_to_term() и term_to_binary() реализованы как BIF, на C, и хорошо оптимизированны по скорости и памяти…


Недостаток ext. term formаt-а: Всякая гибкость имеет обратную сторону. В случае кривых рук или бестолкового программирования можно напаковать таких структур, с которыми эрланговский сервер просто не будет знать что делать.

За пределами статьи остались форматы double чисел, безразмерных целых, передача сжатых данных, упаковка binary в этот формат и т.д. и т.п. Но все это хорошо описано в документации.

Главное понимать идею.
Теги:
Хабы:
Всего голосов 31: ↑31 и ↓0+31
Комментарии4

Публикации

Истории

Ближайшие события

7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн
7 – 8 ноября
Конференция «Матемаркетинг»
МоскваОнлайн
15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань