Как разработать гибкий API для создания стека протоколов Erlang - PullRequest
8 голосов
/ 30 октября 2010

Неудовлетворенный моим текущим подходом, я просто пытаюсь изменить способ построения стеков протоколов в Erlang. Функция, упорядоченная по важности:

  1. Производительность

  2. Гибкость и скорость реализации с добавлением новых вариантов протокола

  3. Это помогло бы разработке изучить варианты протокола из оболочки

Моя текущая модель ( alreday, описанная в этом вопросе ) достигает своих пределов, помимо уродливой асимметрии send () при вызове функции и получении по сообщению.

Общая картина всего механизма протокола выглядит следующим образом:

Нижняя часть:

  • Есть несколько портов или, возможно, иногда gen_tcp внизу каждого стека (есть несколько одинаковых стеков для независимых каналов, поэтому мы не можем быть слишком статичными, просто регистрируя процессы, везде нужно передавать Pids.

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

Верхняя часть:

  • Инициируется возникновением события (в общем смысле, не в смысле event_handler), когда протоколы, ориентированные на установление соединения, заканчиваются (например, с семантикой connect() и close().

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

  • В настоящее время планируется передать список имен модулей + необязательные параметры с верхнего уровня, которые потребляются, пока connect() вызывается вниз по стеку.

  • Процессы верхнего уровня будут связаны, поэтому, если здесь что-то пойдет не так, произойдет сбой соединения.

Типы модулей и виды связи между ними

На данный момент найдено несколько видов модулей:

  • Фильтрующие модули без сохранения состояния

  • Модули с состоянием, некоторые соответствуют gen_server, некоторые gen_fsm, но большинство, вероятно, будут простыми серверными циклами, поскольку выборочный прием будет полезен и довольно часто упрощает код.

Типы связи между слоями:

  • Независимая отправка и получение пакетов (независимо от внешнего вида)

  • Синхронные вызовы, которые что-то отправляют, блокируют до получения ответа и затем возвращают результат в качестве возвращаемого значения.

  • Мультиплексоры, которые взаимодействуют с несколькими модулями (это мое определение здесь, чтобы облегчить обсуждение)

  • Демультиплексоры, которые имеют разные точки присоединения (в настоящее время именуются атомами) для связи с модулями, обращенными вверх.

В настоящее время мои единственные демультиплексоры находятся в статической нижней части стека, а не в динамически создаваемой верхней части. В настоящее время мультиплексоры находятся только в верхней части.

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

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

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

1 Ответ

2 голосов
/ 30 октября 2010

Я добавлю то, что я запланировал, так как часть ответов:

  • connect получает список модулей для стека, который выглядит как проплист в случае параметров, например:

    connect([module1, module2, {module3, [params3]}], param0, further_params)
    

    каждый слой снимает голову и вызывает соединение следующих слоев.

  • connect() «как-то» передает забавные ссылки вверх и / или вниз по слоям

    • отправка для асинхронной отправки по стеку будет возвращена соединением нижнего уровня
    • recv для асинхронного получения стека будет передано в качестве параметра для соединения нижнего уровня
    • вызов синхронизации отправки и ожидание возврата ответа - не уверен, как с этим справиться, возможно, также возвращен из более низкого уровня подключения
  • Списки маршрутизации мультиплексоров могут выглядеть следующим образом

    connect([module1, multiplexer, [[m_a_1, m_a_2, {m_a_3, [param_a_3]}], 
                                    [m_b_1, m_b_2],
                                    [{m_c_1, [param_c_1]}, m_c_2]], param0, 
                                                                    further_params]).
    

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

В данном случае приведен пример реализации идеи для модуля, который не имеет состояния: encode/1 и decode/1 выполняют некоторые преобразования для и обратно пакетов, например. разбирать двоичное представление в записи и обратно:

connect(Chan, [Down|Rest], [], Recv_fun) ->
    {Down_module, Param} = case Down of
                               {F, P} -> {F, P};
                               F when is_atom (F) -> {F, []}
                           end,
    Send_fun = Down_module:connect(Chan, Rest, Param,
                                   fun(Packet) -> recv(Packet, Recv_fun) end),
    {ok, fun(Packet) -> send(Packet, Send_fun) end}.

send(Packet, Send_fun) ->
    Send_fun(encode(Packet)).

recv(Packet, Recv_fun) ->
    Recv_fun(decode(Packet)).

Как только у меня будет пример с состоянием, я тоже его опубликую.

...