Tarantool Cartridge:三行 Lua 後端分片

Tarantool Cartridge:三行 Lua 後端分片

我們 Mail.ru Group 有 Tarantool - 這是 Lua 中的應用程序服務器,它也是一個組合數據庫(反之亦然?)。 它既快又酷,但單個服務器的可能性仍然不是無限的。 垂直擴展也不是萬能的,所以 Tarantool 有水平擴展的工具 - vshard 模塊 [1]。 它允許您將數據分片到多個服務器上,但您必須對其進行修改才能設置並修改業務邏輯。

好消息:我們已經收集了錐體(例如 [2], [3])並提交了另一個框架,該框架將顯著簡化該問題的解決方案。

Tarantool 墨盒 是一個用於開發複雜分佈式系統的新框架。 它允許您專注於編寫業務邏輯而不是解決基礎設施問題。 下面我會告訴大家這個框架是如何工作的,以及如何用它來編寫分佈式服務。

問題到底是什麼?

我們有一隻狼蛛,我們有一個 vshard - 您還想要什麼?

首先,這是一個方便的問題。 vshard 配置是通過 Lua 表進行配置的。 為了使多個 Tarantool 進程的分佈式系統正常工作,各處的配置必須相同。 沒有人願意手動完成。 因此,各種腳本、Ansible、部署系統都被使用。

Cartridge 本身管理 vshard 配置,它基於自己的配置來執行此操作 自己的分佈式配置。 基本上,它是一個簡單的 YAML 文件,其副本存儲在 Tarantool 的每個實例中。 簡化在於框架本身監視其配置並確保它在各處都相同。

其次,這又是一個方便的問題。 vshard配置與業務邏輯的開發無關,只會分散程序員的工作注意力。 當我們討論項目的架構時,我們通常討論的是各個組件及其交互。 現在考慮為 3 個數據中心部署集群還為時過早。

我們一次又一次地解決了這些問題,在某個時候,我們成功地開發了一種方法,使我們能夠在應用程序的整個生命週期中簡化工作:創建、開發、測試、CI/CD、維護。

Cartridge 為每個 Tarantool 進程引入了角色的概念。 角色是允許開發人員專注於編寫代碼的概念。 項目中可用的所有角色都可以在 Tarantool 的單個實例上運行,這足以進行測試。

Tarantool 墨盒的主要特點:

  • 自動化集群編排;
  • 通過新角色擴展應用程序功能;
  • 用於開發和部署的應用程序模板;
  • 內置自動分片;
  • 與 Luatest 測試框架集成;
  • 使用WebUI和API進行集群管理;
  • 打包和部署工具。

你好,世界!

我迫不及待地想展示框架本身,所以我們將把有關架構的故事留到以後,從簡單的開始。 假設 Tarantool 本身已經安裝,唯一剩下要做的就是

$ tarantoolctl rocks install cartridge-cli
$ export PATH=$PWD/.rocks/bin/:$PATH

這兩個命令將安裝命令行實用程序並允許您從模板創建第一個應用程序:

$ cartridge create --name myapp

這就是我們得到的:

myapp/
├── .git/
├── .gitignore
├── app/roles/custom.lua
├── deps.sh
├── init.lua
├── myapp-scm-1.rockspec
├── test
│   ├── helper
│   │   ├── integration.lua
│   │   └── unit.lua
│   ├── helper.lua
│   ├── integration/api_test.lua
│   └── unit/sample_test.lua
└── tmp/

這是一個帶有“Hello, World!”的 git 存儲庫。 應用。 安裝依賴項(包括框架本身)後,讓我們立即嘗試運行它:

$ tarantoolctl rocks make
$ ./init.lua --http-port 8080

這樣,我們就有了未來分片應用程序的一個節點正在運行。 好奇的外行人可以立即打開 Web 界面,用鼠標從一個節點配置集群並享受結果,但現在高興還為時過早。 到目前為止,應用程序還不知道如何做任何有用的事情,所以我稍後會討論部署,但現在是時候編寫代碼了。

應用程式開發

想像一下,我們正在設計一個項目,該項目應該每天接收數據、保存數據並構建報告。

Tarantool Cartridge:三行 Lua 後端分片

我們開始繪製圖表並在其上放置三個組件:網關、存儲和調度程序。 我們正在研究架構。 由於我們使用 vshard 作為存儲,因此我們將 vshard-router 和 vshard-storage 添加到架構中。 網關和調度程序都不會直接訪問存儲,有一個路由器,它是為此創建的。

Tarantool Cartridge:三行 Lua 後端分片

該圖仍然不能完全準確地反映我們將在項目中創建的內容,因為組件看起來很抽象。 我們仍然需要了解如何將其投影到真正的 Tarantool 上 - 讓我們按進程對組件進行分組。

Tarantool Cartridge:三行 Lua 後端分片

將 vshard-router 和 gateway 保留在單獨的實例上沒有什麼意義。 如果這已經是路由器的責任,為什麼我們需要再次遍歷網絡呢? 它們必須在同一進程中運行。 即gateway和vshard.router.cfg都在一個進程中初始化,並讓它們在本地交互。

在設計階段,使用三個組件很方便,但作為開發人員,在編寫代碼時,我不想考慮運行 Tarnatool 的三個實例。 我需要運行測試並檢查我拼寫的 gateway 是否正確。 或者也許我想向我的同事演示一項功能。 我為什麼要費心部署三個實例? 角色的概念就這樣誕生了。 角色是一個常規的luash模塊,其生命週期由Cartridge管理。 在這個例子中,有四個——網關、路由器、存儲、調度器。 另一個項目中可能還有更多。 所有角色都可以在一個進程中運行,這就足夠了。

Tarantool Cartridge:三行 Lua 後端分片

當部署到登台或生產時,我們將根據硬件功能為每個 Tarantool 進程分配自己的一組角色:

Tarantool Cartridge:三行 Lua 後端分片

拓撲管理

有關哪些角色正在運行的位置的信息必須存儲在某個地方。 這個“某處”是一個分佈式配置,我在上面已經提到過。 其中最重要的是集群的拓撲。 以下是 3 個 Tarantool 進程的 5 個複制組:

Tarantool Cartridge:三行 Lua 後端分片

我們不想丟失數據,因此我們要處理有關正在運行的進程的信息。 Cartridge 通過兩階段提交來跟踪配置。 一旦我們想要更新配置,它首先檢查所有實例是否可用並準備好接受新配置。 之後,第二階段應用配置。 因此,即使一份副本暫時不可用,也不會發生什麼壞事。 該配置根本不適用,您會提前看到錯誤。

拓撲部分還包含每個複制組的領導者這樣重要的參數。 這通常是正在寫入的實例。 其餘的通常是只讀的,但也可能有例外。 有時勇敢的開發人員不怕衝突,可以並行地將數據寫入多個副本,但有些操作無論如何都不應該執行兩次。 為此,有一個領導者的標誌。

Tarantool Cartridge:三行 Lua 後端分片

角色的一生

為了使抽象角色存在於這樣的架構中,框架必須以某種方式管理它們。 當然,無需重新啟動 Tarantool 進程即可進行管理。 有 4 個用於管理角色的回調。 Cartridge 本身將根據其在分佈式配置中寫入的內容來調用它們,從而將配置應用於特定角色。

function init()
function validate_config()
function apply_config()
function stop()

每個角色都有其功能 init。 當啟用角色或重新啟動 Tarantool 時,它會被調用一次。 例如,初始化 box.space.create 很方便,或者調度程序可以啟動一些後台 Fiber 以按一定的時間間隔執行工作。

單一功能 init 可能還不夠。 Cartridge 允許角色利用用於存儲拓撲的分佈式配置。 我們可以在同一配置中聲明一個新的部分,並在其中存儲業務配置片段。 在我的示例中,這可以是數據模式,也可以是調度程序角色的調度設置。

集群調用 validate_config и apply_config 每次分佈式配置發生變化時。 當通過兩階段提交應用配置時,集群會檢查每個角色是否已準備好接受此新配置,並在必要時向用戶報告錯誤。 當大家都認為配置正常後,那麼 apply_config.

角色也有方法 stop這是清理角色的重要部分所需要的。 如果我們說該服務器上不再需要調度程序,它可以停止它啟動的那些光纖 init.

角色可以相互交互。 我們習慣在Lua中編寫函數調用,但可能會出現這個過程中沒有我們需要的角色。 為了方便通過網絡進行調用,我們使用了rpc(遠程過程調用)輔助模塊,該模塊是在Tarantool內置的標準netbox的基礎上構建的。 例如,如果您的網關想要直接要求調度程序立即完成工作,而不是等待一天,這可能會很有用。

還有一點很重要,就是保證容錯能力。 Cartridge使用SWIM協議來監控健康狀況 [4]。 簡而言之,進程通過 UDP 相互交換“謠言”——每個進程告訴其鄰居最新消息,然後它們做出響應。 如果突然沒有答案,塔蘭圖爾就會開始懷疑出了什麼問題,過了一會兒,他就會背誦死亡並開始告訴周圍的每個人這個消息。

Tarantool Cartridge:三行 Lua 後端分片

基於該協議,Cartridge組織自動故障處理。 每個進程都會監視其環境,如果領導者突然停止響應,則副本可以接管其角色,並且 Cartridge 相應地配置運行角色。

Tarantool Cartridge:三行 Lua 後端分片

這裡需要小心,因為頻繁的來回切換可能會導致複製過程中的數據衝突。 隨意開啟自動故障轉移當然是不值得的。 你需要清楚地了解正在發生的事情,並確保在領導者恢復並將王冠歸還給他後復制不會中斷。

從以上所有內容中,您可能會覺得角色就像微服務。 從某種意義上說,它們只是作為 Tarantool 進程內的模塊。 但也存在一些根本性的差異。 首先,所有項目角色必須位於同一代碼庫中。 所有 Tarantool 進程必須從相同的代碼庫運行,這樣就不會出現像我們嘗試初始化調度程序但它根本不存在那樣的意外情況。 另外,您不應允許代碼版本存在差異,因為在這種情況下系統的行為很難預測和調試。

與 Docker 不同,我們不能只獲取角色的“映像”,將其帶到另一台機器上並在那裡運行。 我們的角色並不像 Docker 容器那樣孤立。 此外,我們不能在同一個實例上運行兩個相同的角色。 角色要么存在,要么不存在,從某種意義上說,它是一個單例。 第三,在整個複制組內,角色必須相同,否則就會很荒謬——數據相同,但配置不同。

部署工具

我承諾展示 Cartridge 如何幫助部署應用程序。 為了讓其他人的生活更輕鬆,該框架打包了 RPM 包:

$ cartridge pack rpm myapp -- упакует для нас ./myapp-0.1.0-1.rpm
$ sudo yum install ./myapp-0.1.0-1.rpm

安裝的軟件包幾乎包含您需要的所有內容:應用程序和安裝的 luash 依賴項。 Tarantool 也會作為 RPM 包的依賴項來到服務器,我們的服務就可以啟動了。 這是通過 systemd 完成的,但首先您需要編寫一些配置。 至少指定每個進程的 URI。 舉個例子,三個就足夠了。

$ sudo tee /etc/tarantool/conf.d/demo.yml <<CONFIG
myapp.router: {"advertise_uri": "localhost:3301", "http_port": 8080}
myapp.storage_A: {"advertise_uri": "localhost:3302", "http_enabled": False}
myapp.storage_B: {"advertise_uri": "localhost:3303", "http_enabled": False}
CONFIG

這裡有一個有趣的細微差別。 我們不只指定二進制協議端口,而是指定進程的整個公共地址,包括主機名。 這是必要的,以便集群節點知道如何相互連接。 使用 0.0.0.0 作為advertise_uri 是一個壞主意,它應該是公共IP,而不是綁定套接字。 沒有它,什麼都不會工作,所以 Cartridge 根本不會讓你啟動帶有不正確的advertise_uri的節點。

現在配置已準備就緒,您可以啟動進程。 由於通常的 systemd 單元不允許啟動多個進程,因此 Cartridge 上的應用程序是通過所謂的方式安裝的。 實例化單元的工作方式如下:

$ sudo systemctl start myapp@router
$ sudo systemctl start myapp@storage_A
$ sudo systemctl start myapp@storage_B

在配置中,我們指定了 Cartridge 為 Web 界面提供服務的 HTTP 端口 - 8080。讓我們去看看:

Tarantool Cartridge:三行 Lua 後端分片

我們看到這些進程雖然正在運行,但尚未配置。 墨盒還不知道誰應該與誰復制,也無法自行做出決定,所以它正在等待我們的行動。 我們沒有太多選擇:新集群的生命週期從第一個節點的配置開始。 然後我們將剩下的添加到集群中,並給他們分配角色,此時就可以認為部署成功完成了。

經過一周漫長的工作後,倒一杯您最喜歡的飲料並放鬆一下。 該應用程序可以運行。

Tarantool Cartridge:三行 Lua 後端分片

結果

那麼結果呢? 嘗試、使用、留下反饋、在 github 上啟動票證。

引用

[1] Tarantool » 2.2 » 參考 » Rocks 參考 » 模塊 vshard

[2] 我們如何基於 Tarantool 實現 Alfa-Bank 投資業務的核心

[3] 新一代計費架構:過渡到 Tarantool 的轉型

[4] SWIM-集群構建協議

[5] GitHub - tarantool/cartridge-cli: GitHub - tarantool/cartridge-cli

[6] GitHub - tarantool/cartridge: 開發者社區

來源: www.habr.com

添加評論