【问题标题】:How to design a flexible Erlang protocol stack creation API如何设计灵活的 Erlang 协议栈创建 API
【发布时间】:2011-05-02 20:45:12
【问题描述】:

对我目前的方法不满意,我只是想重新设计我在 Erlang 中构建协议栈的方式。按重要性排序的特征:

  1. 性能

  2. 增加新协议变体的灵活性和实施速度

  3. 从 shell 探索协议变体将有助于开发

我当前的模型 (alreday described in this question) 除了通过函数调用发送()和通过消息接收的丑陋不对称外,已经达到了极限。

整个协议引擎的整体图是这样的:

底部:

  • 在每个堆栈的底部有几个端口,或者有时可能还有一个 gen_tcp(对于独立通道有多个相同的堆栈,所以我们在这里不能太静态,只是注册进程,必须到处传递 Pid。

  • 在端口之上是一些由主管管理的模块(从系统开始,在没有错误的情况下保持整个生命周期)。

顶部:

  • 由事件发生(一般意义上不是 event_handler 意义上的)触发的是面向连接的协议结束(例如,connect()close() 语义。

  • 协议栈的顶端可能只能动态启动,因为堆叠在一起形成堆栈的模块是动态可配置的,并且可能会因连接而异。

  • 目前的计划是从顶层传递一个模块名称列表 + 可选参数,在调用 connect() 时从堆栈向下调用。

  • 将链接顶级进程,因此当此处出现任何问题时,整个连接都会失败。

模块的类型和它们之间的通信类型

目前发现有几种模块:

  • 无状态过滤器模块

  • 具有状态的模块,一些适合 gen_server,一些适合 gen_fsm,但大多数可能是简单的服务器循环,因为选择性接收将很有用并经常简化代码。

层之间的通信类型:

  • 数据包的独立发送和接收(从外部看是独立的)

  • 发送内容的同步调用,阻塞直到有答案,然后将结果作为返回值返回。

  • 与多个模块通信的多路复用器(这是我在这里的定义,以方便讨论)

  • 具有不同连接点(目前由原子命名)的多路分解器可与朝上的模块通信。

目前我唯一的解复用器位于堆栈的静态底部,而不是动态创建的顶部。多路复用器目前仅在顶部。

在我之前链接的问题处理的答案和 cmets 中,我听说 API 通常应该只包含函数而不是消息,我同意这一点,除非另有说服力。

请原谅对问题的冗长解释,但我认为它仍然适用于各种协议实现。

我将在答案中写下我迄今为止的计划,还将解释最终的实现和我稍后的经验,以便在这里实现通常有用的东西。

【问题讨论】:

    标签: erlang protocols erlang-otp


    【解决方案1】:

    作为答案的一部分,我将把我计划的内容扔进去:

    • connect 被传递给堆栈的模块列表,在参数的情况下看起来像一个 proplist,例如:

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

      每一层剥去头部并调用下一层连接。

    • connect()“不知何故”将有趣的引用向上和/或向下传递

      • send for async 向下发送堆栈将由较低级别的连接返回
      • 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/1decode/1 对数据包进行一些 for 和 back 转换,例如将二进制表示解析为记录并返回:

    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)).
    

    只要我有一个有状态的示例,我也会发布它。

    【讨论】:

      猜你喜欢
      • 2016-02-10
      • 2013-07-10
      • 2022-01-14
      • 1970-01-01
      • 1970-01-01
      • 2011-10-09
      • 2020-09-29
      • 2018-12-31
      • 1970-01-01
      相关资源
      最近更新 更多