[Erlang 0123] Erlang EPMD


 epmd進程和Erlang節點進程如影隨形,在Rabbitmq集群,Ejabberd集群,Couchbase集群產品文檔中都會有相當多的內容講epmd,epmd是什么呢?
  epmd 是Erlang Port Mapper Daemon的縮寫,全稱足夠明確表達它的功能了(相比之下,OTP就是一個難以從字面理解的名字);epmd完成Erlang節點和IP,端口的映射關系,比如在我的測試機上,
 
[root@nimbus data2]# epmd -names
epmd: up and running on port 4369 with data:
name ns_1 at port 21101
name babysitter_of_ns_1 at port 21100
name ligaoren at port 51056

 

新啟動一個節點之后,再看下epmd的情況:
 
[root@nimbus data2]# erl -name test@nimbus -setcookie 1234 
[root@nimbus ~]# epmd -names
epmd: up and running on port 4369 with data:
name test at port 35441
name ns_1 at port 21101
name babysitter_of_ns_1 at port 21100
name ligaoren at port 51056

 

epmd什么時候啟動?
 
     文檔里面說的是" if the node is to be distributed ",其實從實際操作的角度看,只要啟動時候啟動選項包含-name 或者-sname就會自動啟動epmd;如果由於意外關閉了epmd進程,可以通過/usr/local/lib/erlang/erts-6.0/bin/epmd -daemon 啟動epmd(注意版本不同路徑也會不同).下面我們分別通過erl -sname tt 和 erl 啟動兩個節點,通過observer看下兩種方式啟動之后的應用程序結構,比較一下可以發現,前者多啟動了net_kernel和erl_epmd進程.
 
 
 
如何讓epmd只偵聽指定的IP
 
要實現這個目標,有兩種方式,1.使用環境變量
 
ERL_EPMD_ADDRESS=127.0.0.1 epmd -daemon

 

或者使用啟動參數
 
epmd -address IPList 
 
或者
 
erl ... -kernel inet_dist_use_interface "{127,0,0,1}" 

 

 
如何指定Erlang節點互聯的動態端口范圍
 
  從上面的簡單實驗可以看到每個分布式節點啟動之后,都會在epmd一個動態的端口用來節點間通信.在實際的環境中,我們不可能在防火牆里面把所有的端口都開放出來,那么怎么限制Erlang節點使用的端口范圍呢?答案就是 inet_dist_listen_min inet_dist_listen_max 選項
 
erl -sname abc  -kernel inet_dist_listen_min 4370 inet_dist_listen_max 4375
erl -sname node1  -kernel inet_dist_listen_min 4370 inet_dist_listen_max 4375
erl -sname node2  -kernel inet_dist_listen_min 4370 inet_dist_listen_max 4375
erl -sname node3  -kernel inet_dist_listen_min 4370 inet_dist_listen_max 4375
erl -sname node4  -kernel inet_dist_listen_min 4370 inet_dist_listen_max 4375
erl -sname node5  -kernel inet_dist_listen_min 4370 inet_dist_listen_max 4375

 

 
在啟動上面節點的時候,我們顯示指定了kernel的 inet_dist_listen_min inet_dist_listen_max值,也就是節點可偵聽端口的最小值,最大值.上面節點啟動成功之后,我們通過epmd -names查看一下端口注冊情況
 
epmd: up and running on port 4369 with data:
name node5 at port 4375
name node4 at port 4374
name node3 at port 4373
name node2 at port 4372
name node1 at port 4371
name abc at port 4370

 

 
這時,我們嘗試再創建一個節點試一下
erl -sname node6  -kernel inet_dist_listen_min 4370 inet_dist_listen_max 4375

 

失敗了,錯誤信息節錄如下:
 
{error_logger,{{2014,7,3},{20,51,4}},"Protocol: ~tp: register/listen error: ~tp~
n",["inet_tcp",eaddrinuse]}
{error_logger,{{2014,7,3},{20,51,4}},crash_report,[[{initial_call,{net_kernel,in
it,['Argument__1']}},{pid,<0.20.0>},{registered_name,[]},{error_info,{exit,{erro
r,badarg},[{gen_server,init_it,6,[{file,"gen_server.erl"},{line,320}]},{proc_lib
,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,239}]}]}},{ancestors,[net_sup,ke
rnel_sup,<0.10.0>]},{messages,[]},{links,[<0.17.0>]},{dictionary,[{longnames,fal
se}]},{trap_exit,true},{status,running},{heap_size,610},{stack_size,27},{reducti
ons,1861}],[]]}

 

 
是的,啟動失敗的原因是在epmd注冊失敗,沒有可用的動態端口可以分配給新節點了,所以報出的是地址正在使用的錯誤.
 
注:上面參數修改如果是在代碼中完成,如下
 application:set_env(kernel, inet_dist_listen_min, 9100).
 application:set_env(kernel, inet_dist_listen_max, 9105). 

 

  這個在 Erlang FAQ中有提到  http://www.erlang.org/faq/how_do_i.html 5.18  ...run distributed Erlang through a firewall?
 
如果是配置在Confige文件中,配置節為:
{ kernel, [
            {inet_dist_listen_min, 6000},
            {inet_dist_listen_max, 7999}
          ]}

 

 
LYSE里面就給出了使用配置文件的路子,只不過他是把這個配置放在專門的配置文件
如果這兩個參數調整了,最好干掉epmd,重新啟動,之所以這樣是因為epmd在所有節點關閉之后還會存在,所以必須重啟以便新參數生效.
 
如何讓epmd使用指定端口
默認情況下epmd使用的TCP端口是4369
ERL_EPMD_ADDRESS=127.0.0.1 ERL_EPMD_PORT=8384 epmd -daemon 
交互模式下要鏈接指定的端口可以使用port選項
 epmd  -port 8384 -names

 

調試狀態看細節 

如果啟動epmd -d 啟動調試,可以看到輸出信息;下面的過程,我逐一啟動了abc,xyz,test三個節點;然后關閉掉xyz,test節點,從下面的輸出信息,可以看到節點注冊和注銷注冊的情況.
 
 
[root@Slave4 ~]#
[root@Slave4 ~]# epmd -d
epmd: Thu Jul  3 15:56:15 2014: epmd running - daemon = 0
epmd: Thu Jul  3 15:56:25 2014: ** got ALIVE2_REQ
epmd: Thu Jul  3 15:56:25 2014: registering 'abc:2', port 35383
epmd: Thu Jul  3 15:56:25 2014: type 77 proto 0 highvsn 5 lowvsn 5
epmd: Thu Jul  3 15:56:25 2014: ** sent ALIVE2_RESP for "abc"
epmd: Thu Jul  3 15:56:43 2014: ** got ALIVE2_REQ
epmd: Thu Jul  3 15:56:43 2014: registering 'xyz:2', port 42802
epmd: Thu Jul  3 15:56:43 2014: type 77 proto 0 highvsn 5 lowvsn 5
epmd: Thu Jul  3 15:56:43 2014: ** sent ALIVE2_RESP for "xyz"
epmd: Thu Jul  3 15:57:22 2014: ** got ALIVE2_REQ
epmd: Thu Jul  3 15:57:22 2014: node name already occupied abc
epmd: Thu Jul  3 15:57:22 2014: ** sent ALIVE2_RESP for "abc"
epmd: Thu Jul  3 15:57:22 2014: trying to unregister node with unknown file descriptor 6
epmd: Thu Jul  3 15:57:51 2014: ** got ALIVE2_REQ
epmd: Thu Jul  3 15:57:51 2014: registering 'test:1', port 32781
epmd: Thu Jul  3 15:57:51 2014: type 77 proto 0 highvsn 5 lowvsn 5
epmd: Thu Jul  3 15:57:51 2014: ** sent ALIVE2_RESP for "test"
epmd: Thu Jul  3 15:58:23 2014: ** got PORT2_REQ
epmd: Thu Jul  3 15:58:23 2014: ** sent PORT2_RESP (ok) for "test"
epmd: Thu Jul  3 16:05:26 2014: unregistering 'xyz:2', port 42802
epmd: Thu Jul  3 16:05:35 2014: unregistering 'test:1', port 32781

 

 
 是不是比較迷惑里面的ALIVE2_REQ之類的是什么意思?這就要認真對照Erlang Distribution Protocol了,對照下面的圖,如果有興趣可以研究下協議,地址:  http://www.erlang.org/doc/apps/erts/erl_dist_protocol.html  
 
IMAGE MISSING
 
 
最后,這里有一個Golang的項目 Eclus-EPMD replacement in Go 有興趣的可以看下,項目地址: https://github.com/goerlang/eclus  
 
參考資料:
 
 


免責聲明!

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



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