概述
分散式平臺的核心在於併發,容錯。 而 Elixir 的優勢正是在於對於併發和容錯的處理。
分散式模型
- CSP(Communicating Sequential Process) 模型 :: 多個程式通過管道(channel)進行互動
- Actor 模型 :: 每個程式管理自己的內部狀態,通過訊息和外界互動
對於 CSP 來說,重點在與 channel,通過 channel 管理併發的任務,並不關心執行任務的執行者。 Actor 模型關心的是任務的執行者(也就是 Actor),每個 Actor 都是可以通過訊息和外界互動,根據訊息完成任務。
Elixir 採用的是 Actor 模型,golang 的併發是 CSP 模型。
併發
並行(concurrency)經常會和並行(parallelize)的概率混淆,併發是指同時發生,並行是指同時執行, 比如,在一個 4core 的機器上,併發的量可能上萬,但是並行可能只有 4。
Elixir 的併發是由 BEAM VM 來管理的,理論上,一個 BEAM VM 上可以建立 268,000,000 個程式,基本就可以當做是無限制。 一般來說,BEAM VM 都是通過少量的排程程式來管理大量的 processes Elixir 的併發程式之間不會共享任何東西(包括記憶體在內)。
容錯
對錯誤的處理方式,最普遍的就是 try/catch,這種方式對於單機的系統來說可以適用,但是對於分散式系統來說,存在不足之處:
- 分散式系統涉及多個系統間的互動,try/catch 只能在單點上捕獲異常
- 分散式系統中可能發生的錯誤往往很難重現,也無法預測,即使捕獲到異常,也很難預置處理方式,也就是很多異常會導致系統無法恢復
- try/catch 機制一般都是捕獲錯誤,然後自己處理或者直接往上層拋異常,隨著系統的複雜,異常的層級也會越來越多
Elixir 中也有類似 try/catch 的機制,但是它的容錯性不是依靠這種機制來保證的。 Elixir 不是致力於去減少錯誤的發生,而是致力於提供一種機制去減少錯誤的影響,並使得系統能夠自己從錯誤中恢復。
Elixir 中通過程式樹來確保整個應用的可用性,少數幾個程式作為 supervisor,用來監管其他完成實際業務的程式。 如果程式出現問題,由 supervisor 程式負責重啟或者銷燬。
除了程式樹,對於分散式系統的容錯,Erlang 的理念是 let it crash 原因也就是因為在分散式系統中,很多的錯誤和環境相關,難於重現,並且很多時候在錯誤的基礎上恢復系統相當困難, 遠遠沒有直接重啟來的簡單有效。
當然,let it crash 不是 let everything crash,以下情況還是要處理:
- 對於關鍵性的程式,是絕對不能讓它 crash 的,比如有些主幹上的 supervisor
- 可以預期的錯誤,就要有對應的錯誤處理,不能什麼都靠重啟來解決
- let it crash 是指把那些無法預期的錯誤交給 supervisor 來處理
OTP 平臺
OTP 平臺的分散式系統核心是 process 和 message Elixir 本身也可以實現非常健壯的分散式系統,但是藉助 OTP 平臺上已經成熟的元件,可以更快的建立一個分散式應用
下面是 OTP 平臺上提供的成熟元件:(各個元件的示例參考:http://www.cnblogs.com/wang_yb/p/5589257.html)
- agent
- GenServer
- GenEvent
- task
- supervisor