Erlang初體驗

jopen 10年前發布 | 65K 次閱讀 Erlang開發 ErLang

我對 Erlang 編程理念的理解:以分布式架構師的角度寫代碼。

函數式編程

Erlang 里面的函數是數學里面的函數:必須有返回值。只要是函數必然有返回值,函數是一個過程,以英文的句號為函數結束符。函數結束之前的表達式就是該函數的返回值。所以這也是在 Erlang 里面的函數不會看到任何 return 語句的原因。 C++ 等其他語言的函數和函數之前可以通過共享變量來實現消息傳遞。 Erlang 里面的函數不可以,消息的傳遞通過函數的傳入和傳出。也只是為什么 Erlang 號稱天生之處并行處理的原因,因為他們不共享變量,也就不需要加鎖。

很多人聽到函數式編程都會覺得高大上或者晦澀難懂。因為函數是編程沒有 for 循環語句,但是在我看來,關鍵在于會使用【列表推倒】和【尾遞歸】來進行循環遍歷。說到函數式編程就會拿快速排序說事,下面這個示例是 Erlang 版本的快速排序:

-module(sort).

-export([qsort/1]).

qsort([]) -> [];
qsort([Pivot | T]) ->
    qsort([X || X <- T, X < Pivot]
         ++ [Pivot] ++
         qsort([X || X <- T, X >= Pivot]).

非常簡潔,[Pivot | T] 就是拿列表的第一個元素當快排中的 Pivot 。

[X || X <- T, X < Pivot]

上式就是【列表推導】,含義就是找出列表 T 中所有元素小于 Pivot 中的元素組成一個新的列表。不過,這個例子顯然性能不高,只是一個示例。

很多人一直在鼓吹函數式語言馬上就要迎來朝陽,但是在我看來,函數式編程永遠只能是小眾語言,這就像當年的 lisp machine ,被鼓吹的天花亂墜還是夭折了。現在主流的計算機架構都是馮諾依曼體系的,并不是最適合函數式語言的生存土壤。

一切都是常量

沒有變量,也就沒有通過變量共享狀態導致的資源競爭,也就不需要加鎖。任何狀態的變化都是通過函數的輸入輸出來進行改變,輕量級進程的狀態變化也是靠消息傳遞(函數的輸入輸出)來實現。這也是為什么有人說函數式編程適合高并發的原因,因為他們沒有變量,一切都是常量。

輕量進程

Erlang 里面有 spawn 函數,可以快速的創建一個 process ,這里的 process 不是操作系統的進程,而是 Erlang 自己的輕量進程。 Erlang 輕量到超乎你想象,構建 kv 數據庫的時候,甚至可以對不同的 key 分配給不同的進程。而且進程的表示單位是 Pid ,只要知道進程的 Pid,哪怕該進程是在別的機器上面,都可以很輕易的發送給它。原因是 Erlang 的【天生自帶RPC通信】和【自帶端口映射】

天生自帶RPC通信

ToPid ! Data

ToPid 是接受方進程的id , Data 可以是 Erlang 的任何類型,比如

Pid ! {name, "yanyiwu.com"}.

也就是可以直接把任何數據結構當成消息發送,天生自帶 RPC 通信。(雖然本來 RPC 的含義是“遠程過程調用”,不過其實反正就是幫你序列化了數據結構,Erlang 的 ! 操作符也是如此。)

進程端口映射

節點之間發消息在代碼里面的表示也還是

ToPid ! Data

也就是在寫代碼的時候,根本不用考慮該進程是在哪臺機器上面,無論是本 Erlang 進程(這里的進程是操作系統級別的進程,不是 Erlang 的輕量進程) 內,還是其他機器的進程,都不用管。這是因為有 epmd 的存在。

Epmd是Erlang Port Mapper Daemon的縮寫,在Erlang集群中相當于dns的作用,供給節點名稱到端口的查詢辦事,epmd綁定在總所周知的4369端口上。

有了 epmd ,寫分布式程序就好像寫單機程序一樣簡單。

嚴密的模塊化管理

Erlang 的模塊類似 C++ 中的 namespace(命名空間),但是比命名空間更利于高效的軟件工程管理。

在 Erlang 項目源碼中處處可見如下代碼。

-module(my_app).
-export([start/2, stop/1]).

-module 指明模塊名,-export 指明導出的函數。未被導出的函數都無法被外界調用。從軟件工程上看的話,這樣使得模塊功能和使用方法更加清晰。使用者只需要關心如何 -export 里面的函數即可。相比較之下 C++ 對這方面特別不規范,而 Java 通過對類聲明為 public class 指明可以被外界使用,Node.js 也是使用 export 來顯示聲明可以被外界使用的函數。

行為模式

-module(ecomet_app).

-behaviour(application).

%% comment: Application callbacks
-export([start/2, stop/1]).
-behavior(application).

Erlang/otp 里面的【行為模式】概念等價于 OOP 里面的接口概念。上面代碼示例的意思就是該模塊(ecomet_app)遵守的行為模式是(application)。剛行為模式需要實現的兩個接口函數就是 -export([start/2, stop/1]).

另一個示例如下是遵守監督者(supervisor)行為模式,實現的一個接口函數是 -export([init/1]).

-module(ecomet_sup).

-behaviour(supervisor).

%% Supervisor callbacks
-export([init/1]).

監督者機制

Erlang/otp 的天生分布式特性在監督機制里面體現的很好,每一個 otp 應用啟動的時候,都是啟動監督者(supervisor)和工作者(worker)。他們的關系是樹形結構,每個工作者的上級都會有監督者,每個監督者的上級也可能有監督者。當工作者異常退出的時候,監督者會根據相應的參數決定是否對工作者進行重啟。如果重啟失敗的話監督者也會退出,而更加上層的監督者收到信號后會對他們進行重啟等處理。這個監督者機制非常好理解,其實就是 OOP 編程里面的 try ... catch 異常處理機制。當出現異常的時候一層一層的往上拋出,直到有人重啟。

otp平臺

Erlang 最強大的地方也是最讓我感覺難學的地方,就是它的 otp 平臺。各種行為模式,讓我感覺就像多年以前學習 MFC 的時候,感覺很強大,但是卻總是感覺自己被按死在一條特定的軌道上面奔跑,有種不自由的疲憊感。

代碼熱切換

熱切換也叫熱升級,大部分情況下,如果需要對 C++/Java 程序進程版本升級,則需要重啟進程。 Erlang 支持熱切換的意思就是可以在運行的時候進行代碼升級。升級過程不影響進程的運行,而且在過渡階段新舊版本還可以共存。是不是碉堡了。這個功能對于那些需要 7x24 高可用的服務來說簡直就是爽爆了。

Erlang 進程本身可以通過一個類似“后門”的控制臺 erl 來實時的查看狀態,甚至直接使用控制臺來修改配置等,非常方便,這對于大部分其他語言來說,簡直就是黑魔法般神奇的存在。

典型缺點

  • 文檔太少,出現問題搜索出來的答案也少。
  • Erlang 人才稀缺,招聘不易。
  • 動態語言最典型的就是調試不易。
  • 上手門檻較高。

最后,我只是 Erlang 的入門初學者,因為工作中需要使用 ejabberd (Erlang 的開源項目),從而學習了 Erlang ,欠缺實戰經驗,所以這篇文章標題起為 【Erlang初體驗】。

原文地址:http://yanyiwu.com/work/2014/11/26/erlang-otp-experience.html

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!