[Erlang 0101] Gproc:擴展進程注冊機制


 2013-3-25 22:45:01更新:抱歉 抱歉 腦子里面想的是進程注冊 手誤 一直把進程注冊寫成了進程字典

Erlang 進程注冊機制 目前的限制是:
  • names只能是atom
  • 一個進程只能注冊一個name
  • 不能進行高效的搜索和遍歷,進程信息的檢索是通過遍歷檢查進程的元數據完成的.
 Ulf T. Wiger的開源項目  Gproc 就是解決上面問題的,難得的是這個項目的文檔,范例,測試代碼相當完整,還有 專門的論文講述整個項目的來龍去脈,設計取舍.
 
Gproc是Erlang進程注冊機制的加強版,提供了如下原生進程注冊沒有的功能:
  • 使用任意Erlang Term作為進程的別名
  • 一個進程注冊多個別名
  • 支持QLC和match specification高效查詢
  • 自動移交已經注冊的names和屬性到另外的進程
  • .....
那它是怎么做的呢?這些額外的信息是維護在哪里,是ETS嗎?動手操練一下,驗證想法:
 
Eshell V5.9  (abort with ^G)
1> application:start(gproc).
ok
2> ets:i().
id              name              type  size   mem      owner
----------------------------------------------------------------------------
13              code              set   265    10683    code_server
4110            code_names        set   53     7018     code_server
8207            shell_records     ordered_set 0      73       <0.26.0>
ac_tab          ac_tab            set   9      929      application_controller
file_io_servers file_io_servers   set   0      283      file_server_2
global_locks    global_locks      set   0      283      global_name_server
global_names    global_names      set   0      283      global_name_server
global_names_ext global_names_ext  set   0      283      global_name_server
global_pid_ids  global_pid_ids    bag   0      283      global_name_server
global_pid_names global_pid_names  bag   0      283      global_name_server
gproc           gproc             ordered_set 0      73       gproc_sup
gproc_monitor   gproc_monitor     ordered_set 0      73       gproc_monitor
inet_cache      inet_cache        bag   0      283      inet_db
inet_db         inet_db           set   29     554      inet_db
inet_hosts_byaddr inet_hosts_byaddr bag   0      283      inet_db
inet_hosts_byname inet_hosts_byname bag   0      283      inet_db
inet_hosts_file_byaddr inet_hosts_file_byaddr bag   0      283      inet_db
inet_hosts_file_byname inet_hosts_file_byname bag   0      283      inet_db
ok

 

 
   應用程序啟動之后我們查看ETS表的信息,發現多出來兩個表:gproc和gproc_monitor;下面我們就使用Shell進程完成測試,為當前的Shell進程創建別名"Shell",並向它發送消息dvd,之后再shell中flush()查看已經接收到的消息.

5> gproc:reg({p,l,"Shell"}).
true
6> gproc:send({p,l,"Shell"},dvd).
dvd
7> flush().
Shell got dvd
ok

 

 現在查看一下ETS表的內容,發現里面已經記錄了當前Shell進程的注冊信息;
 
9> ets:i(gproc).
<1   > {{<0.43.0>,l}}
<2   > {{<0.43.0>,{p,l,"Shell"}},[]}
<3   > {{{p,l,"Shell"},<0.43.0>},<0.43.0>,undefined}
EOT  (q)uit (p)Digits (k)ill /Regexp -->q
ok

 

緊接上面我們為Shell再創建一個別名,然后查看ETS
 
10> gproc:reg({p,l,"Shell_alia"}).
true
11> ets:i(gproc).
<1   > {{<0.43.0>,l}}
<2   > {{<0.43.0>,{p,l,"Shell"}},[]}
<3   > {{<0.43.0>,{p,l,"Shell_alia"}},[]}
<4   > {{{p,l,"Shell"},<0.43.0>},<0.43.0>,undefined}
<5   > {{{p,l,"Shell_alia"},<0.43.0>},<0.43.0>,undefined}
EOT  (q)uit (p)Digits (k)ill /Regexp -->q
ok
 

 

下面簡單演示一下QLC:
 
40>  Q5 = qlc:q([P || {{p,l,'_'},P,C} <- gproc:table( )]).
{qlc_handle,{qlc_lc,#Fun<erl_eval.20.111823515>,
                    {qlc_opt,false,false,-1,any,[],any,524288,allowed}}}
41>
41> qlc:eval(Q5).
[<0.65.0>,<0.61.0>]
42> qlc:e(qlc:q([N || N <- gproc:table()])).
[{{p,l,"Hello"},<0.65.0>,undefined},
{{p,l,"NEW_Process"},<0.61.0>,undefined}]

 

上面的幾段代碼基本上包含了它最重要的feature,我們看下實現,注冊name的過程實際上是把注冊信息寫到了ETS;而發送消息的第一步就是從ETS表中查詢name對應的Pid,然后進行發送.以發送為例看一下代碼實現:
 
https://github.com/esl/gproc/blob/master/src/gproc.erl
 
%% If Key belongs to a unique object (name or aggregated counter), this
%% function will send a message to the corresponding process, or fail if there
%% is no such process. If Key is for a non-unique object type (counter or
%% property), Msg will be send to all processes that have such an object.
%% @end
%%
send(Key, Msg) ->
    ?CATCH_GPROC_ERROR(send1(Key, Msg), [Key, Msg]).

send1({T,C,_} = Key, Msg) when C==l; C==g ->
    if T == n orelse T == a ->
            case ets:lookup(?TAB, {Key, T}) of
                [{_, Pid, _}] ->
                    Pid ! Msg;
                _ ->
                    ?THROW_GPROC_ERROR(badarg)
            end;
       T==p orelse T==c ->
            %% BUG - if the key part contains select wildcards, we may end up
            %% sending multiple messages to the same pid
            lists:foreach(fun(Pid) ->
                                  Pid ! Msg
                          end, lookup_pids(Key)),
            Msg;
       true ->
            erlang:error(badarg)
    end;
send1(_, _) ->
    ?THROW_GPROC_ERROR(badarg).
 

 

 
細致的實現
 
 下面的測試我們通過gproc:info查看進程信息,下面的結果注意一下current function屬性值,是不是有疑問?
 
 
17> P2=spawn(fun() -> gproc:reg({p,l,"NEW_Process"}),receive a -> a end end ).
<0.61.0>
18> ets:i(gproc).
<1   > {{<0.61.0>,l}}
<2   > {{<0.61.0>,{p,l,"NEW_Process"}},[]}
<3   > {{{p,l,"NEW_Process"},<0.61.0>},<0.61.0>,undefined}
EOT  (q)uit (p)Digits (k)ill /Regexp -->q
ok
19> gproc:info(P2).
[{gproc,[{{p,l,"NEW_Process"},undefined}]},
{current_function,{erl_eval,receive_clauses,6}},
{initial_call,{erlang,apply,2}},
{status,waiting},
{message_queue_len,0},
{messages,[]},
{links,[]},
{dictionary,[]},
{trap_exit,false},
{error_handler,error_handler},
{priority,normal},
{group_leader,<0.25.0>},
{total_heap_size,610},
{heap_size,233},
{stack_size,8},
{reductions,100},
{garbage_collection,[{min_bin_vheap_size,46368},
                      {min_heap_size,233},
                      {fullsweep_after,65535},
                      {minor_gcs,1}]},
{suspending,[]}]

 

 
這里內部實現還是做得非常細致的,不是簡單的把信息附加在Process_info,比如對current function信息做了修正.
 
https://github.com/esl/gproc/blob/master/src/gproc.erl
 
%% We don't want to return the internal gproc:info() function as current
%% function, so we grab the 'backtrace' and extract the call stack from it,
%% filtering out the functions gproc:info/_ and gproc:'-info/1-lc...' entries.
%%
%% This is really an indication that wrapping the process_info() BIF was a
%% bad idea to begin with... :P
%%
info_cur_f(T, Default) ->
    {match, Matches} = re:run(T,<<"\\(([^\\)]+):(.+)/([0-9]+)">>,
                     [global,{capture,[1,2,3],list}]),
    case lists:dropwhile(fun(["gproc","info",_]) -> true;
                   (["gproc","'-info/1-lc" ++ _, _]) -> true;
                   (_) -> false
               end, Matches) of
     [] ->
         Default;
     [[M,F,A]|_] ->
         {current_function,
          {to_atom(M), to_atom(F), list_to_integer(A)}}
    end.

 

 
好了今天就到這里.
 
 
最后小圖一張:
 
 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM