簡體   English   中英

初學者的Erlang / OTP行為

[英]Erlang/OTP behaviors for beginner

正如我從“Erlang and OTP in action”一書中所理解的那樣,行為一詞指的是:

  • 行為界面,這是一組功能;
  • 行為實現,即特定於應用程序的代碼(回調模塊);
  • 行為容器,這是一個過程。

題:

Erlang / OTP初學者應該了解哪些行為? 是否有可能簡單地描述和理解OTP行為的概念?

什么'回調函數'在Elang / OTP的上下文中實際意味着什么?

我們可以考慮行為實現中的回調,因為Java中的方法會覆蓋嗎?

該書說,以下代碼中庫函數'gen_server:start_link / 4'的關聯回調函數是'Module:init / 1'。

這是否意味着使用init / 1我們調用gen_server:start_link / 4庫函數? 或者這意味着什么呢?

-module(tr_server).

-behaviour(gen_server).

-include_lib("eunit/include/eunit.hrl").

%% API
-export([
         start_link/1,
         start_link/0,
         get_count/0,
         stop/0
         ]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
         terminate/2, code_change/3]).

-define(SERVER, ?MODULE).
-define(DEFAULT_PORT, 1055).

-record(state, {port, lsock, request_count = 0}).


%%%===================================================================
%%% API
%%%===================================================================


%%--------------------------------------------------------------------
%% @doc Starts the server.
%%
%% @spec start_link(Port::integer()) -> {ok, Pid}
%% where
%%  Pid = pid()
%% @end
%%--------------------------------------------------------------------
start_link(Port) ->
    gen_server:start_link({local, ?SERVER}, ?MODULE, [Port], []).

%% @spec start_link() -> {ok, Pid}
%% @doc Calls `start_link(Port)' using the default port.
s    tart_link() ->
    start_link(?DEFAULT_PORT).

%%--------------------------------------------------------------------
%% @doc Fetches the number of requests made to this server.
%% @spec get_count() -> {ok, Count}
%% where
%%  Count = integer()
%% @end
%%--------------------------------------------------------------------
get_count() ->
    gen_server:call(?SERVER, get_count).

%%--------------------------------------------------------------------
%% @doc Stops the server.
%% @spec stop() -> ok
%% @end
%%--------------------------------------------------------------------
stop() ->
    gen_server:cast(?SERVER, stop).


%%%===================================================================
%%% gen_server callbacks
%%%===================================================================

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

handle_call(get_count, _From, State) ->
    {reply, {ok, State#state.request_count}, State}.

handle_cast(stop, State) ->
    {stop, normal, State}.

handle_info({tcp, Socket, RawData}, State) ->
    do_rpc(Socket, RawData),
    RequestCount = State#state.request_count,
    {noreply, State#state{request_count = RequestCount + 1}};
handle_info(timeout, #state{lsock = LSock} = State) ->
    {ok, _Sock} = gen_tcp:accept(LSock),
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%%%===================================================================
%%% Internal functions
%%%===================================================================

do_rpc(Socket, RawData) ->
    try
        {M, F, A} = split_out_mfa(RawData),
        Result = apply(M, F, A),
        gen_tcp:send(Socket, io_lib:fwrite("~p~n", [Result]))
    catch
        _Class:Err ->
            gen_tcp:send(Socket, io_lib:fwrite("~p~n", [Err]))
    end.

split_out_mfa(RawData) ->
    MFA = re:replace(RawData, "\r\n$", "", [{return, list}]),
    {match, [M, F, A]} =
        re:run(MFA,
               "(.*):(.*)\s*\\((.*)\s*\\)\s*.\s*$",
                   [{capture, [1,2,3], list}, ungreedy]),
    {list_to_atom(M), list_to_atom(F), args_to_terms(A)}.

args_to_terms(RawArgs) ->
    {ok, Toks, _Line} = erl_scan:string("[" ++ RawArgs ++ "]. ", 1),
    {ok, Args} = erl_parse:parse_term(Toks),
    Args.


%% test

start_test() ->
    {ok, _} = tr_server:start_link(1055).

我將嘗試用簡單的術語解釋行為背后的基礎知識,並讓您在理解這些基礎知識的基礎上回答自己的問題,而不是像其他答案那樣嘗試解決您的具體問題。

行為基本上是一個消息處理框架,其中“框架”是指可以由最終用戶完成和定制的問題的部分解決方案的經典定義。 OTP行為基本上提供:

  • 消息循環
  • 與底層OTP集成,支持代碼升級,跟蹤,系統消息等。

行為將消息處理委托給回調模塊,或者將行為實現委托為“Erlang和OTP In Action”來調用它們。 在調用其init/1函數時,回調模塊通常會為消息循環創建狀態以代表它。 然后,行為循環將此狀態傳遞給回調模塊消息處理函數的每個后續調用,並且這些調用中的每一個都可以返回修改后的狀態。 回調函數還返回指令,告訴行為消息循環下一步做什么。

以下是行為核心的消息循環的大大簡化版本:

loop(Callbacks, State) ->
  {Next, NState} =
 receive
                     M1 ->

                       Callbacks:handle_m1(M1,State);
                     M2 ->
                       Callbacks:handle_m2(M2,State);
                     Other ->
                       Callbacks:handle_other(Other,State)
                   end,
  case Next of

    stop -> ok;
    _ -> loop(Callbacks, NState)
  end.

這個尾遞歸循環將Callbacks模塊和State變量作為參數。 在首次調用此循環之前,您已經告訴行為您的回調模塊是什么,然后基本OTP行為支持代碼已經調用了您的init/1回調函數來獲取State的初始值。

我們的示例行為循環接收表單M1M2和任何其他消息的消息,其詳細信息在此處無關緊要,並且對於每個消息,在Callbacks模塊中調用不同的回調函數。 在此示例中, handle_m1handle_m2回調函數分別處理消息M1M2 ,而回調handle_other處理所有其他類型的消息。 請注意, State被傳遞給每個回調函數。 每個函數都應該返回一個元組,第一個元素告訴循環下一步該做什么,第二個元素包含循環的可能新狀態 - 與State相同的值或新的不同值 - 循環存儲在其變量中NState 在此示例中,如果Next是原子stop ,則循環停止,但如果它是其他任何東西,則循環以遞歸方式調用自身,將新狀態NState傳遞給下一次迭代。 由於它是尾遞歸的,所以循環不會吹出堆棧。

如果你仔細研究標准OTP行為的來源,比如gen_servergen_fsm ,你會發現很像這樣的循環,但是由於處理系統消息,超時,跟蹤,異常等,它們要復雜得多。標准行為也是如此在一個單獨的進程中啟動它們的循環,因此它們還包含用於啟動循環進程並將消息傳遞給它的代碼。

問: Erlang / OTP初學者應該了解哪些行為? 是否有可能簡單地描述和理解OTP行為的概念?

行為通常在代碼中使用,以便編譯器可以根據其行為生成更直觀的錯誤消息,即application / supervisor / gen_server / gen_event / gen_fsm。

它使編譯器能夠提供特定於ex:gen_server行為的錯誤消息

問:在Elang / OTP環境中,“回調函數”實際意味着什么?

可以說回調函數取自GUI編程(至少類似)。 每當事件發生在前。 鼠標單擊有一個單獨的功能,可以處理鼠標點擊。

因此,無論何時例如。 從另一個模塊調用gen_server的導出函數,該函數可以具有具有不同模式的回調函數(handle_call / handle_cast)。

問:我們可以將行為實現中的回調視為在Java中重寫的方法嗎?

是啊......也許......不:)

問:本書說下面代碼中庫函數'gen_server:start_link / 4'的關聯回調函數是'Module:init / 1'。

gen_server:start_link自己調用init函數,由w55回答....(對不起,這是一個很大的名字)。


希望我已回答你所有的疑問:)

Erlang / OTP初學者應該了解哪些行為?

可能是這里寫的。

是否有可能簡單地描述和理解OTP行為的概念?

從文檔中讀取:“行為是這些常見模式的形式化。我們的想法是在通用部分(行為模塊)和特定部分(回調模塊)中划分流程的代碼。”

什么'回調函數'在Elang / OTP的上下文中實際意味着什么?

請查看上面的鏈接,其中提供了回調函數的示例。

我們可以考慮行為實現中的回調,因為Java中的方法會覆蓋嗎?

在Java術語中,行為可能是Java接口,而回調則是接口中定義的方法之一的實現。

該書說,以下代碼中庫函數'gen_server:start_link / 4'的關聯回調函數是'Module:init / 1'。 這是否意味着使用init / 1我們調用gen_server:start_link / 4庫函數? 或者這意味着什么呢?

這意味着,每次調用gen_server:start_link / 4時,都會調用函數Module:init / 1,其中Module是傳遞給start_link函數的第二個參數,其中您提供的參數為第四個參數。 換句話說,這是start_link / 4幕后發生的事情:

...
start_link(Name, Module, Args, Opts) ->
  ...
  Module:init(Args)
  ...
...

查看erlang lib目錄中gen_server模塊的源代碼。 它在源代碼中得到了很好的解釋,評論非常精細。

gen_server:start_link調用init。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM