麻烦了解Erlang Gen

我正处于学习Erlang的初级阶段,我需要一些进一步的帮助。 不知道这是否会得到任何阳光,但在这里它...我正在寻找一个关于如何工作的流程图。

示例代码:https://github.com/erlware/Erlang-and-OTP-in-Action-Source/blob/master/chapter_03/tr_server.erl

让我解释我的问题...

1> tr_server:start_link().

我明白这一点,它调用start_link(?DEFAULT_PORT),它调用gen_server:start_link - 并且实际上将回调到tr_server(?MODULE)init([Port])。

init([Port]) ->
    {ok, LSock} = gen_tcp:listen(Port, [{active, true}]),
    {ok, #state{port = Port, lsock = LSock}, 0}.

这也是理解的。 你发送数据到服务器,gen_server:handle_info / 2被处理,因此调用?MODULE:handle_info / 2 - 它是一个例子,并且由于我们在?MODULE:init中返回了一个超时值,它将匹配handle_info超时,#状态{lsock = LSock} =状态)。

好的,这是有道理的。

这是我开始对Erlang流程感到困惑的地方

几天后,我一直在阅读关于此的在线资源(包括Erlang和OTP-in-action) - 此示例源自此 - 还有:http://learnyousomeerlang.com/clients-and-servers

我不确定Erlang服务器的流程如何工作。 我的理解是,任何发送到服务器的消息都会被gen_server:handle_info / 2处理,如果它们超出限制的话 - 意味着它们没有配置或匹配任何其他gen_server:handle_call / 3? 这意味着,任何TCP数据都由gen_server自动处理:handle_info / 2 - 它将回调到?MODULE:handle_info?

我不明白的是handle_call,handle_cast在服务器体系结构中的方式和位置 - 我是否理解服务器从客户机 - >服务器体系结构的流程(直到我感到困惑)。 我认为这非常重要,以便像电路图一样说明流程图。

以下是主要问题:当客户端发送以下内容时,服务器的流程如何:

lists:reverse([1,2,3]).

在纯文本中,获得流程图以了解其工作原理将会很好。 从案文和案例来看,它的工作原理并不十分清晰。 我们不明白为什么需要:

get_count() ->
    gen_server:call(?SERVER, get_count).

stop() ->
    gen_server:cast(?SERVER, stop).

我很欣赏任何答案,我知道可以用尽解释! 对不起,有任何语法错误!


看起来你对tcp端口和服务器通过handle_info回调处理数据的情况有很好的了解。 这是Erlang代码和连接到端口的某个外部客户端之间的一种客户端/服务器交互。 但是在Erlang系统中,你也在Erlang进程之间有客户端/服务器关系,双方都在运行Erlang代码。 (即使它只是gen_server进程和Erlang命令shell进程。)

当你使用gen_server:call / cast客户端函数时,它们以你永远不会看到的方式来包装你的消息,但接收的gen_server进程会识别这个消息,并用它来分类消息,然后将解包的消息传递给相应的handle_call / handle_cast。 除此之外,流量与tcp端口上的传入数据相同:在这两种情况下,它只是向服务器发送异步消息,正在接收和分派到正确的功能。 同时在客户端,gen_server:call()函数将等待回复(发件人的Pid包含在包装器中),而gen_server:cast()立即执行。

这些只是便利功能。 原则上,gen_server可以只处理一个回调来处理所有类型的消息,让您自己决定是调用还是投射以及如何反应。 但是通过提供这些库函数并为您分类消息,它可以降低处理类似演员的电话的风险,反之亦然,或者将带外消息与正确的呼叫/转换相混淆。 在所有情况下,流程都是相同的:客户端 - >服务器 - >回叫[ - >服务器回复 - >客户端]。

因此,你可以使用?SERVER ! {get_count, self()}实现get_count()函数?SERVER ! {get_count, self()} ?SERVER ! {get_count, self()} ,在handle_info()回调中处理该消息,而不是在handle_call()中处理该消息。 (只要不要忘记将回复发送回邮件中包含的Pid,否则客户端将永久停留。)

或者您可以完全跳过像get_count()这样的完整用户API函数,并告诉您的用户只需将{get_count,self()}发送到服务器进程并等待回复(其形状也必须记录在案)。 但是之后你无法改变这些消息如何在外壳下看到的细节。 gen_server:call / cast函数可以帮助您隐藏如此混乱的实现细节,并使其不太可能破坏客户端/服务器通信。

希望这可以帮助。


我正处于学习Erlang的初级阶段,我需要一些进一步的帮助

  • 看看一些简单的非gen_server客户端 - 服务器示例。 尝试为您自己的客户端服务器想出一个简单的想法并编写代码。
  • 了解如何使用模块名称对简单服务器进行参数化。
  • 了解gen_server和行为。
  • 练习将简单的服务器转换为gen_server。 使用带分割窗口的文本编辑器非常方便。
  • 了解gen_tcp和套接字。
  • 查看将gen_tcp和套接字与gen_server结合在一起的示例。
  • 看这里:

    http://erlang.org/doc/design_principles/des_princ.html

    我不会从第6步开始,你似乎在做。

    这意味着,任何TCP数据都由gen_server自动处理:handle_info / 2 - 它将回调到?MODULE:handle_info?

    没有回调。 TCP数据绕过整个gen_server架构。 TCP数据与其他入侵者一起进入后门。 所以gen_server:handle_info()在那里处理它们。 handle_info()检查服务器邮箱中是否有与指定为handle_info()参数的模式相匹配的消息。

    任何需要对TCP数据进行的操作都是在handle_info()内完成的。 当然,如果你需要在handle_info()中做一些复杂的数据处理,你总是可以调用helper函数来计算一些结果:

    handle_info({tcp, Socket, RawData}, State) ->
        Result1 = computerInGermanyProcess(RawData),
        Result2 = computerInFranceProcess(RawData),
        Result3 = computerInRussiaProcess(RawData),    
        Result4 = State#state.stored_val,
    
        gen_tcp:send(Socket, [Result1, Result2, Result3, Result4]),
        {noreply, State};  %%Because a TCP message landed in the mailbox with no From value, 
                           %%do not reply to From, and do not mess with the value in State.
    
     computerInGermanyProcess(RawData) ->
              %% Possibly use gen_tcp and sockets again to send a message
              %% to another computer to get some data in order to
              %% calculate Result1:
              Result1.
     computerInFranceProcess(RawData) ->
              ...
              Result2.
     computerInRussiaProcess(RawData) ->
              ...
              Result3.         
    

    我不明白的是handle_call,handle_cast在服务器体系结构中的方式和位置 - 我是否理解服务器从客户机 - >服务器体系结构的流程(直到我感到困惑)。 我认为这非常重要,以便像电路图一样说明流程图。

    Client:                                                                    
    +------------------------------------------------------+------------------------------------------------------+
    | my_request() ->                                      |   handle_call({dostuff, Val}, ClientPid, State) ->   |
    |     Request = {dostuff, 10},                         |       %%Do something with Val, State                 |
    |     Server = ?MODULE,                                |       Response = {result, 45},                       |
    |     Response = gen_server:call(Server, Request).     |       NewState = ....,                               |
    |                            |                         |       {Response, NewState}.                          |
    |                            |       from gen_server:  |                                                      |
    |                            |            start_link() |                                     ^                |
    |                            |                 |       |                                     |                |
    +----------------------------+-----------------+-------+-------------------------------------+----------------+
                                 |                 |                                             |
                                 |                 |                                             |
    +----------------------------+-----------------+-------+                                     |
    |-module(gen_server).        |                 |       |                                     |
    |-export([call/2,....]).     V                 |       |                                     |
    |                                              |       |                                     |
    |call(Server, Request) ->                      V       |                                     |
    |  Server ! {request, Request, call, self(), Module} --+-->+                                 |                     
    |  receive                                             |   |                                 ^                
    |      {reply, Response, Server} ->                    |   |                                 |
    |          Response      ^                             |   V                                 |
    |  end.                  |                             |   |                                 |
    +------------------------+-----------------------------+   |                                 |
    |   Mailbox              |                             |   |                                 |
    |                        |                             |   |                                 |
    |       {reply, Response, Server}  <----------<--------+---+--------------<--------------+   |
    |                                                      |   V                             ^   ^      
    +------------------------------------------------------+   |                             |   |
                                                               |                             |   |
                                                               |                             |   |
    Server:                                                    |                             |   |
    +------------------------------------------------------+   |                             |   |
    |    Mailbox                                           |   |                             |   |
    |                                                      |   V                             ^   ^
    |        {request, Request, call, ClientPid, Module} <-+---+                             |   |
    |                            |                         |                                 |   |
    +----------------------------+-------------------------+-----------------------------+   |   |                 
    |                            |                                                       |   |   |
    |loop(State) ->              |                                                       |   |   |
    |    receive                 V                                                       |   ^   ^
    |        {request, Request, call, ClientPid, Module}  ->                             |   |   |           ^
    |            {Response, NewState} = Module:handle_call(Request, ClientPid, State} ---+---|-->+           |
    |            ClientPid ! {reply, Response, self()}, ----------->---------------------+-->+            To Client
    |            loop(NewState);                                                         |                   ^
    |        {request, Request, cast, ClientPid, Module} ->                              |                   |
    |            NewState = Module:handle_cast(Request, State), ------->---------->------|----->------------>+
    |            loop(NewState);                                                         |
    |        ...                                                                         |
    |        ...                                                                         |                                      
    |    end.                                                                            |
    +------------------------------------------------------------------------------------+
    

    客户端调用gen_server:call()时的流程gen_server:call()

  • 客户端调用gen_server:start_link() ,它至少指定了定义handle_call / handle_cast函数的模块。

  • 客户端调用gen_server:call(ServerName, Request) ,通常包含在接口函数中。

  • gen_server:call(ServerName,Request)被定义为send a message to the server ,如下所示:

     ServerName ! {request, Request, call, self(), ModName}.
    

    ModName先前已绑定到gen_server中指定的原子:start_link():第二个参数是指定包含函数handle_call(),handle_cast()等定义的模块名称的位置。

  • 当服务器收到该消息时,服务器调用ModName:handle_call() ,并且您的ModName:handle_call()代码执行Request:

    handle_call(Request, ClientPid, ServerLoopValue) ->
        %%Compute some result using information in Request/ServerLoopValue
    
  • ModName:handle_call()函数的最后一行告诉服务器将什么发送回客户端作为a response

      {Response, NewServerLoopValue}.
    

    然后服务器做这样的事情:

      From ! {reply, Response, ServerPid}.
      loop(NewServerLoopValue).
    

    并且NewServerLoopValue成为服务器的循环()的新全局变量。 每个服务器都有一个loop()函数,它看起来像这样:

    loop(ServerLoopValue) ->
        receive
            {request, dothis, From} ->
                Result1 = ...SomeValue + 5....,
                From ! {Result1, self()},
                loop(NewServerLoopValue);
            {request, {dothat, 10}, From} ->
                Result2 = ... SomeValue - 10...,
                From ! {Result2, self()},
                loop(NewServerLoopValue);
            {request, stop, From}
                %%do not call loop()
        end.
    

    ServerLoopValue就像一个全局变量,所有不同的请求都可以看到。 各种gen_server请求处理程序可以使用存储在ServerLoopValue中的信息来计算响应,也可以将信息添加到其他请求处理程序将来可以使用的ServerLoopValue中。

  • 使用带有{active, true}, {packet, 4}的TCP套接字的gen_server后门进程:

  • 客户端调用gen_tcp:send()

  • 在服务器的套接字结束处,Erlang从套接字读取数据,构造一个消息元组,并将消息元组放入server's mailbox

  • 服务器从邮箱中检索{tcp,...}消息并调用handle_info()

  • handle_info()调用gen_tcp:send(Socket, Response)将响应发送回客户端。

  • handle_info()的最后一行告诉服务器在调用服务器的loop()函数时使用什么值:

    {noreply, SomeValue}   =>  loop(SomeValue)
    
  • 使用带有{active, false}, {packet, 0}的TCP套接字的gen_server后门进程:

    Erlang gen_tcp没有收到任何东西

    链接地址: http://www.djcxy.com/p/38193.html

    上一篇: Trouble Understanding Erlang Gen

    下一篇: sqlite3 sqlite3:table