《Erlang/OTP併發編程實戰》第八章 分佈式 Erlang/OTP 簡介

  1. 在 Erlang 中沒有共享,只有消息傳遞,因此分佈式還是單機本質上沒有什麼區別。
  2. Erlang 集羣是一個全聯通網絡。
  3. 節點啓動:
    1. erl -name xx
      適用於配有 DNS 的普通網絡環境,需要給出節點的完全限定域名
    2. erl -sname xx
      適用於完全限定域名不可用的情況
  4. 採用短節點名和長節點名的節點所處的通信模式是不同的,它們之間無法形成集羣。
  5. 隱形節點:
    藉助一些特殊的節點,我們可以將多個集羣合併成更大的、非全聯通的集羣。
    這類節點經過特殊配置,不會對外傳播其他節點的信息,它們甚至可以對其他節點隱身,以便對集羣進行非侵入式監控。
  6. 建立連接:
    net_adm:ping('[email protected]').

     

  7. EPMD(Erlang 端口映射守護進程):
    1. 啓動每一個節點,EPMD 都會檢查本地機器上是否運行着 EPMD,如果沒有,節點就會自動啓動 EPMD;
    2. EPMD 會追蹤在本地機器上運行的每個節點,並記錄分配給它們的端口號;
    3. 當一臺機器上的 Erlang 節點試圖與遠程節點通信時, 本地的 EPMD 就會聯絡遠程機器上的 EPMD(默認使用TCP/IP,端口號4369),詢問在遠程機器上有沒有相應名字的節點。如果有,遠程 EPMD 就會回覆一個端口號,通過該端口便可直接與遠程節點通信;
    4. EPMD 不會主動搜尋其他 EPMD,只有在某個節點主動搜尋其他節點時才能建立。
  8. 通信安全:
    Erlang 默認的分佈式模型基於這樣一個假設,集羣中的所有節點都運行在一個受信網絡內。
    如果這個假設不成立,或者其中的某些機器需要與外界通信,那麼就應該直接在TCP(或 UDP、SCTP 等)之上配合恰當的應用層協議來實現非受信網絡上的通信。此外還可以利用 SSL、SSH、IPsec 等技術建立加密隧道,甚至直接將 Erlang 的分佈式通信層架設在 SSL 等傳輸協議之上。
  9.  
    auth:get_cookie().            % 獲取當前節點的 cookie
    set_cookie(Node, Cookie).     % 設置 cookie

     

  10. 在 Erlang 中,shell 是採用進程來容錯的。shell 進程崩潰後,信箱中的內容會丟失,但變量綁定關係仍然會保留。
  11. 遠程使用 shell:
    1. 節點之間事先無須建立連接
    2. Ctrl-G 命令下:r '[email protected]'
    3. 如果節點名中含有句點,要加上單引號
    4. 命令不會立即見效
    5. 退出遠程 shell 時注意:!!!不要用 q(). q() 是關閉執行該命令的節點,也就是遠程節點。
      要想安全退出,要使用 Ctrl-G 或 Ctrl-C
  12. 資源探測應用:
    1. 資源探測:建立資源提供方和資源使用方之間的關係
    2. 代碼實現:
      -module(resource_discovery).
      
      -behavior(gen_server).
      %% API
      -export([start_link/0, add_target_resource_type/1, add_local_resource/2, fetch_resources/1, trade_resources/0]).
      
      -export([
        init/1,
        handle_call/3,
        handle_cast/2,
        handle_info/2,
        terminate/2,
        code_change/3
      ]).
      
      -define(SERVER, ?MODULE).
      
      -record(state, {target_resource_types, local_resource_tuples, found_resource_tuples}).
      
      start_link() ->
        gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
      
      init([]) ->
        {ok, #state{target_resource_types = [], local_resource_tuples = dict:new(),
          found_resource_tuples = dict:new()}}.
      
      add_target_resource_type(Type) ->
        gen_server:cast(?SERVER, {add_target_resource_type, Type}).
      
      add_local_resource(Type, Instance) ->
        gen_server:cast(?SERVER, {add_local_resource, {Type, Instance}}).
      
      fetch_resources(Type) ->
        gen_server:call(?SERVER, {fetch_resources, Type}).
      
      trade_resources() ->
        gen_server:cast(?SERVER, trade_resources).
      
      
      handle_cast({add_target_resource_type, Type}, State) ->
        TargetTypes = State#state.target_resource_types,
        NewTargetTypes = [Type | lists:delete(Type, TargetTypes)],
        {noreply, State#state{target_resource_types = NewTargetTypes}};
      
      handle_cast({add_local_resource, {Type, Instance}}, State) ->
        ResourceTuples = State#state.local_resource_tuples,
        NewResourceTuples = add_resource(Type, Instance, ResourceTuples),
        {noreply, State#state{local_resource_tuples = NewResourceTuples}};
      
      handle_cast(trade_resources, State) ->
        ResourceTuples = State#state.local_resource_tuples,
        AllNodes = [node() | nodes()],
        lists:foreach(
          fun(Node) ->
            gen_server:cast({?SERVER, Node}, {trade_resources, {node(), ResourceTuples}})
          end, AllNodes),
        {noreply, State};
      
      handle_cast({trade_resources, {ReplyTo, Remotes}}, State) ->
        #state{local_resource_tuples = Locals, target_resource_types = TargetTypes, found_resource_tuples = OldFound} = State,
        FilteredRemotes = resource_for_types(TargetTypes, Remotes),
        NewFound = add_resources(FilteredRemotes, OldFound),
        case ReplyTo of
          noreply ->
            ok;
          _ ->
            gen_server:cast({?SERVER, ReplyTo}, {trade_resources, {noreply, Locals}})
        end,
        {noreply, State#state{found_resource_tuples = NewFound}}.
      
      handle_call({fetch_resources, Type}, _From, State) ->
        {reply, dict:find(Type, State#state.local_resource_tuples), State}.
      
      handle_info(_, State) ->
        {stop, normal, State}.
      
      terminate(_Reason, _State) ->
        sc_store:delete(self()),
        ok.
      
      code_change(_OldVsn, State, _Extra) ->
        {ok, State}.
      
      
      add_resources([{Type, Resource} | T ], ResourceTuples) ->
        add_resources(T, add_resource(Type, Resource, ResourceTuples));
      add_resources([], ResourceTuples) ->
        ResourceTuples.
      
      resource_for_types(Types, ResourceTuples) ->
        Fun =
          fun(Type, Acc) ->
            case dict:find(Type, ResourceTuples) of
            {ok, List} ->
              [{Type, Instance} || Instance <- List] ++ Acc;
            error ->
              Acc
            end
          end,
        lists:foldl(Fun, [], Types).
      
      add_resource(Type, Resource, ResourceTuples) ->
        case dict:find(Type, ResourceTuples) of
          {ok, ResourceList} ->
            NewList = [Resource | lists:delete(Resource, ResourceList)],
            dict:store(Type, NewList, ResourceTuples);
          error ->
            dict:store(Type, [Resource], ResourceTuples)
        end.

       

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章