Erlang/OTP 有別於大多數的程式環境,即使是那些也使用虛擬機器的環境。Erlang 對於應用程式的建構方式、所應具備的隔離層級,以及 Erlang 虛擬機器可執行哪些內容,以及軟體可執行哪些內容,都有堅定的看法。它不只是一種程式語言,而是一個建構系統的完整架構。了解其核心原則有助於快速上手,而無須在之後重寫所有內容:它能確保所有應用程式都能完美結合,更新作業可以持續進行,且程式碼容易建構且可觀察。
在本章中,我們將涵蓋 Erlang 虛擬機器以及 OTP 的最高階核心概念。
Erlang 執行時期系統
所有內容的基本架構為 Erlang 虛擬機器本身,稱為 BEAM。技術上而言,BEAM 為 Erlang 虛擬機器的單一實作,因此也會有其他實作方式。例如,Erllvm 是一種針對 LLVM 的實作方式(使用一些自訂修補程式實現所有功能),而 90 年代的舊實作方式稱為 JAM。Erlang 虛擬機器使用 C 實作,並包含許多精妙的功能:進程執行排程器、垃圾回收、記憶體配置器、用於事件的計時輪、一堆用於抽象作業系統功能並提供統一介面的智慧型切換器(例如,時間管理、檔案處理驅動程式等),一些比 Erlang 本身執行的速度更快的內建函式(BIF),以及用於其他語言(NIF)本機實作函式的介面,以及其專屬排程器。很明顯還有很多內容,但您可以將所有這些東西視為 BSD 或 Linux 中的 kernel:建構更精妙內容所需要的中階內容。
如果你只有虛擬機器而沒有其他東西,就無法執行 Erlang 程式碼。你沒有標準函式庫,甚至沒有函式庫可載入程式碼。為了讓所有程式碼開始執行,需要進行一些複雜的開機準備程式,我們不需要了解這個過程。只要知道搭配虛擬機器搭載的預先載入 Erlang 模組是有限的,這些模組可用於設定網路和檔案處理工具,而這些工具則用於進一步載入並執行模組。不過,如果你對瞭解更多內容有興趣,請參閱 The BEAM Book 或 BEAM Wisdoms。
如果你使用虛擬機器和預先載入的程式,以及所有使程式碼載入成為可能的小型公用程式,那麼你所擁有的基本上就是所謂的 Erlang 執行時期系統 (ERTS)。執行時期系統在啟動時會依照一個稱為 開機腳本 的元件 (沒有人會手寫這個腳本) 的指示操作,其會指定要啟動的內容。
Erlang 預設會提供開機腳本,載入 minimal 的程式碼量,足以啟動 shell 和撰寫你自己的應用程式。完成後,我們可以開始思考 Erlang,而不僅是虛擬機器。
Erlang/OTP
我們到目前為止所描述的程式等同於作業系統核心。現在,我們需要為使用者空間元件建立基礎區塊。在 Erlang 中,這基本上就是 OTP 處理的內容。OTP 指明在虛擬機器上執行的「元件」應該如何建立。除了「處理程序和訊息」之外,這門程式語言還有更多功能:有一套定義良好的方式可讓你建構你的程式碼。
備註
OTP 代表 _開放電信平台_,這在早期愛立信的 Erlang 中,基本上是毫無意義的名稱,用於使所有內容開放原始碼。
Erlang/OTP 系統是以稱為 OTP 應用程式 的元件架構。你安裝的每一個 Erlang 版本或使用它建置的系統都會搭載一些 OTP 應用程式。OTP 應用程式基本上有兩種變形:函式庫應用程式(只是模組的集合)和 可執行應用程式(包含模組的集合,但也會指定儲存在監控樹下的狀態處理程序結構)。為了清楚起見,我們將在這整本書中對 OTP 應用程式使用下列術語:
- 函式庫應用程式:無狀態模組集合
- 可執行應用程式:啟動有狀態監控樹狀結構的 OTP 應用程式,在這些結構中執行處理序
- OTP 應用程式:互換使用 函式庫 或 可執行應用程式
預設情況下,所有人包含的兩個 OTP 應用程式稱為 stdlib
(一個函式庫應用程式,其中包含核心標準函式庫模組,例如 list
或 maps
),以及 kernel
(一個可執行應用程式,為 Erlang 系統設定核心結構,其依賴 OTP 應用程式運作)。
當一個節點開機時,所有必要的 OTP 應用程式中的模組會載入到記憶體中。然後,會啟動 kernel
。從這個時間點開始,kernel
就管理著系統的生命週期。所有的其他 OTP 應用程式及它們的設定都是透過 kernel 處理的,而且配銷和熱式更新等獨特功能也是如此。如果我們回到作業系統比較,你可以將 kernel
OTP 應用程式想成有點類似你會想到的 Linux 核心中的 systemd
(或者如果你討厭 systemd
或使用 BSD,那麼就是 init
,Windows 使用者可以把它想成執行其他服務的服務)
事實上,kernel
和 stdlib
是你只需要用於基本運作的 Erlang shell 的兩個應用程式。當你輸入 erl
(或者在 Windows 上啟動 werl
)時,這會引導 VM 開機,並使用預先載入的 stdlib
載入 kernel。其他的所有東西都是可選的,而且可以在以後載入。
標準的 Erlang 配銷包含以下應用程式
- kernel
- stdlib
- crypto(密碼學基本組元)
- ssl(TLS 終止函式庫)
- inets(網路服務例如 FTP 或 HTTP 客戶端)
- ct(通用測試架構)
- wx(圖形工具組)
- observer(一個使用
wx
建置的控制面板以管理你的 Erlang 節點) - compiler(建立你自己的專案的 Erlang 編譯器)
- 等等
所有這些都被放到一起,形成所謂的 Erlang 版本。版本是 OTP 應用程式的集合,有可能與虛擬機器的完整副本捆綁在一起。因此,當你下載並安裝 Erlang 時,你只會取得一個名稱類似 Erlang/OTP-21.3.4 的版本。你可以自由建置你自己的版本,這會取用標準配銷的某些 OTP 應用程式,然後將它們與你自己的某些應用程式捆綁在一起。
因此,如果我們寫一個名為 proxy
依賴於 ssh
和 ssl
(它們本身依賴於 public_key
、crypto
、stdlib
和 kernel
)的應用程式,我們會製作一個在其中包含所有這些元件的版本
- ERTS
- kernel
- stdlib
- crypto
- public_key
- ssl
- ssh
- proxy
你可以在圖 1 中看到這個的視覺化表示。

圖 1: 建置 proxy
版本的視覺化表示
基本上,建置一個 Erlang 系統是重新捆綁 VM,以及一些隨預設配銷提供的標準應用程式,再連同你自己的應用程式和函式庫。
活在 Erlang/OTP
社區所開發的標準工具,例如 Rebar3 按照您所寫入並發布的是 OTP 應用程式這個想法來執行,且包含與其處理相關所需的所有功能。這對於只要求您在其中一個檔案的某個位置有一個稱為 main()
的函數的許多程式語言來說是一個大幅度的轉變。這也是為什麼程式語言經常稱為 Erlang/OTP
而非僅僅『Erlang』:它不只是一個程式語言,它是一種一般性的開發架構,對您所做的每一件事都命令一些基本的結構。
而且每個人都遵循它,無論他們是撰寫嵌入式軟體、區塊鏈系統或分散式資料庫。就是 OTP,不然什麼都沒有。而其他語言通常不會強制要求進行特定動作才能開始執行,但隨後會在後端新增一些需求(例如與套件管理員整合時),Erlang 及其整個社群期望您只寫入 OTP 應用程式,而其他工具可以處理這些應用程式。
所以快速在 Erlang 中入手的關鍵在於熟悉其架構,這個架構通常被保留為更進階的教材。我們將在這邊採取相反的做法,從一個完全具備功能性的佈署版本開始著手,然後深入探究其結構。接下來的章節將專注於了解如何在這些需求中工作。