Elixir 分散式 Application 故障轉移和接管

developerworks發表於2019-02-16

Elixir 可以執行在主/從, 故障轉移/接管模式下. 要使Elixir應用程式能夠執行故障轉移/接管, Elixir應用程式必須是一個OTP應用程式.

下面來建立一個包含Supervisor的Elixir專案

mix new distro --sup

修改distro.ex新增logger模組. 以記錄當觸發故障轉移/接管操作時的日誌記錄.

defmodule Distro do
  use Application
  require Logger
  def start(type, _args) do
    import Supervisor.Spec, warn: false
    Logger.info("Distro application in #{inspect type} mode")
    children = [
      worker(Distro.Worker, [])
    ]
    opts = [strategy: :one_for_one, name: Distro.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

Distro.Worker是一個GenServer: 它使用全域性名稱註冊, 假設其執行在叢集中的一個節點上, 全域性註冊讓我們不用考慮其實際的執行位置, 只需要提供註冊名稱就可以訪問.

defmodule Distro.Worker do
  use GenServer
  require Logger
  def start_link do
    GenServer.start_link(__MODULE__, [], name: {:global, __MODULE__})
  end
  def init([]) do
    {:ok, [], 1000}
  end
  def handle_info(:timeout, state) do
    Logger.debug "timeout"
    {:noreply, state, 1000}
  end
end

編譯

$ mix compile

應用程式分佈

本節闡述瞭如何把一個應用程式分佈到多個節點上

假設應用程式執行在3個節點上, 名稱分別為abc, bcd, def. 建立三個配置檔案如下:

touch config/abc.config
touch config/bcd.config
touch config/def.config

配置檔案中有3個重要的鍵值對. distributed, sync_nodes_mandatorysync_nodes_timeout, 其中:

  1. distributed 定義了分散式應用的啟動延遲, 備用節點.

  2. sync_nodes_mandatory 定義強制要求的節點

  3. sync_nodes_timeout 定義了distributed中所有節點都啟動完成需要等待的時間, 如果在此時間內要求的節點沒有全部啟動, 那麼所有節點上的應用啟動失敗.

abc.config

[
  {logger,[{console,[{format,<<"$date $time $metadata[$level] $message
">>}]}]},
  {kernel,
    [{distributed, [
        {`distro`, 5000, [`abc@192.168.8.104`, {`bcd@192.168.8.104`, `def@192.168.8.104`}]}]},
        {sync_nodes_mandatory, [`bcd@192.168.8.104`, `def@192.168.8.104`]},
        {sync_nodes_timeout, 30000}
]}].

bcd.config

[
  {logger,[{console,[{format,<<"$date $time $metadata[$level] $message
">>}]}]},
  {kernel,
    [{distributed, [
        {distro,5000, [`abc@192.168.8.104`, {`bcd@192.168.8.104`, `def@192.168.8.104`}]}]},
        {sync_nodes_mandatory, [`abc@192.168.8.104`, `def@192.168.8.104`]},
        {sync_nodes_timeout, 30000}
]}].

def.config

[
  {logger,[{console,[{format,<<"$date $time $metadata[$level] $message
">>}]}]},
  {kernel,
    [{distributed, [
        {distro,5000, [`abc@192.168.8.104`, {`bcd@192.168.8.104`, `def@192.168.8.104`}]}]},
        {sync_nodes_mandatory, [`abc@192.168.8.104`, `bcd@192.168.8.104`]},
        {sync_nodes_timeout, 30000}
]}].

在不同的終端自動全部3個節點

iex --name abc@192.168.8.104 -pa _build/dev/lib/distro/ebin/ --app distro 
--erl "-config config/abc"

iex --name bcd@192.168.8.104 -pa _build/dev/lib/distro/ebin/ --app distro 
--erl "-config config/bcd"

iex --name def@192.168.8.104 -pa _build/dev/lib/distro/ebin/ --app distro 
--erl "-config config/def"

驗證步驟

bcd, defabc 的備用節點. bcd 優先於def, 如果abc死掉, 應用程式會在bcd上重啟, 如果bcd死掉, 應用程式會轉移到def, 這時候abc, bcd 修復啟動後, 應用會被abc接管.

  1. 終止(Ctrl+C兩次)節點abc@192.168.8.104後,5秒內會在節點bcd@192.168.8.104上重啟應用

  2. 再次啟動節點abc@192.168.8.104後,應用在bcd@192.168.8.104上停止, 應用被恢復後的abc@192.168.8.104節點接管(Takeover)

參考資料

  1. Elixir Application Failover/Takeover

  2. Distributed OTP Applications

  3. 原始碼倉庫

  4. 分散式應用

相關文章