Если какой-либо программе необходимо передать данные эрланговсому серверу, эти данные предварительно должны быть сериализованы. То есть преобразованы в бинарный вид, так, чтобы эрланг мог потом распаковать их обратно. Обычно это делают с помощью 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 по своей структуре очень прост. Обычно данные в нем имеют вид «тэг, данные» или «тэг, длина, данные». Тэг описывает какого типа данные запакованы.
Для основных типов данных тэги такие
Перед всеми запакованными данными должен стоять тэг 131. Это номер версии текущего ext. term format.
То есть эрланговское значение [{banknote, 100, rub}] упакуется в стркутуру:
В качестве proof of concept напишем, например, на питоне простую процедуру, которая запаковывет структуры питона в ext term format, и распакуем результат на эрланге…
Будем упаковывать питоновский список в эрланговский список, питоновский tuple в эрланговский tuple, целые числа, питоновские строки. Для атомов… ну… например создадим наследника от строки в питоне, чтобы их легко можно было отличить в процедуре упаковки от обычных строк.
Получается что-то типа:
Отлично. Теперь упакуем какую нибудь сложную структуру и передадим ее в эрланг самым простым способом.
То есть упаковали и послали udp пакет. В эрланге принимаем и расшифровываем:
Мы передали (малой кровью) сложную структуру из питона в эрланг. Достоинства такого подходы —
Недостаток ext. term formаt-а: Всякая гибкость имеет обратную сторону. В случае кривых рук или бестолкового программирования можно напаковать таких структур, с которыми эрланговский сервер просто не будет знать что делать.
За пределами статьи остались форматы double чисел, безразмерных целых, передача сжатых данных, упаковка binary в этот формат и т.д. и т.п. Но все это хорошо описано в документации.
Главное понимать идею.
В качестве альтернативного варианта можно рассмотреть использование эрланговского 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 в этот формат и т.д. и т.п. Но все это хорошо описано в документации.
Главное понимать идею.