|
約定:本文只考慮 Linux 系統(tǒng),文中涉及的“服務(wù)程序”是以 C++ 或 Java 編寫,編譯成二進(jìn)制可執(zhí)行文件(binary 或 jar),程序啟動(dòng)的時(shí)候一般會(huì)讀取配置文件(或者以其他方式獲得配置信息),同一個(gè)程序每個(gè)服務(wù)進(jìn)程的配置文件可能略有不同。“服務(wù)器”這個(gè)詞有多重含義,為避免混淆,本文以 host 指代服務(wù)器硬件,以“服務(wù)端程序/進(jìn)程”指代服務(wù)器軟件(或者具體說 Web Server 和 Sudoku Solver,這兩個(gè)都是服務(wù)軟件)。
在進(jìn)入正題之前,先看一個(gè)虛構(gòu)但典型的例子:Sudoku Solver。(Sudoku Solver 是個(gè)均質(zhì)的無狀態(tài)服務(wù),分布式系統(tǒng)中進(jìn)程的狀態(tài)遷移不是本文的主題。)
假設(shè)你們公司的分布式系統(tǒng)中有一個(gè)專門求解數(shù)獨(dú)(Sudoku)的服務(wù)程序,這個(gè)程序是你們團(tuán)隊(duì)開發(fā)并維護(hù)的。通常 Web Server 會(huì)使用這個(gè) Sudoku Solver 提供的服務(wù),用戶通過 web 頁面提交一個(gè) Sudoku 謎題,web server 轉(zhuǎn)而向 Sudoku Solver 尋求答案。每個(gè) Web Server 會(huì)同時(shí)跟多個(gè) Sudoku Solver 聯(lián)系,以實(shí)現(xiàn)負(fù)載均衡。系統(tǒng)的消息結(jié)構(gòu)大致如下,每個(gè)圓角矩形是一個(gè)進(jìn)程,運(yùn)行在各自的 host 上:
上圖中的 Web Server 請不要簡單理解為 httpd + cgi,它其實(shí)泛指一切客戶端,本身可能是個(gè) stateful 的服務(wù)程序。
當(dāng)然,系統(tǒng)不是一開始就是這樣,它經(jīng)歷了多步演化。
一開始 (a),只有一個(gè) Sudoku Solver,也只有一臺(tái) Web Server,是個(gè)簡單的一對一 (1:1) 的使用關(guān)系;
隨后 (b),隨著業(yè)務(wù)量增加,一臺(tái) host 不堪重負(fù),于是又部署了幾臺(tái) Sudoku Solver,變成了一對多 (1:n) 的使用關(guān)系;
再后來 (c),一臺(tái) Web Server 撐不住了,于是部署了幾臺(tái) Web Server,形成了我們一開始看到的多對多 (m:n) 的使用關(guān)系;
(d) 中的情況留到文末再講。
在分布式系統(tǒng)中部署并運(yùn)行 Sudoku Solver,需要考慮以下幾個(gè)問題:
- Sudoku Solver 如何部署到多臺(tái) host 上運(yùn)行?是把可執(zhí)行文件拷過去嗎?程序用到的庫怎么辦?配置文件怎么辦?
- 如何啟動(dòng)服務(wù)程序 Sudoku Solver ?如果每個(gè) Solver 的配置文件稍有不同(比如每個(gè) Solver 有自己的 service name),那么配置文件是自動(dòng)生成嗎?
- Sudoku Solver 的 listening port 如何配置?如何保證它不與其他服務(wù)程序重復(fù)?
- 如果程序 crash,誰來重啟?能否自動(dòng)重啟?開發(fā)/運(yùn)維人員能否及時(shí)收到 alert?
- 如果想主動(dòng)重啟 Sudoku Solver,要不要登錄到那臺(tái) host 上去 kill ?還是能夠遠(yuǎn)程控制?
- 如果要升級 Sudoku Solver 程序,如何重新部署?如何(盡量)做到不中斷服務(wù)?
- Web Server 如何知道那些 Sudoku Solver 的地址?是不是靜態(tài)寫到 Web Server 的配置文件里?
- 如果 Sudoku Solver 所在的 host 發(fā)生硬件故障,管理人員是否能立刻得知這一狀況?Web Server 能否自動(dòng) fail over 到其他 alive 的 Solver 上?
- 部署新的 Sudoku Solver 之后,Web Server 能否自動(dòng)開始使用新的 Solver 而無需重啟?(重啟 Web Server 似乎不是大問題,這里我們進(jìn)一步考慮 client 是個(gè)有狀態(tài)的服務(wù),應(yīng)該盡量避免重啟。)
- 程序可否安全地退役?比方說公司不再做求解 Sudoku 的業(yè)務(wù),那么關(guān)閉全部 Sudoku Solver 會(huì)不會(huì)對其他業(yè)務(wù)造成影響?
這些問題可以大致歸結(jié)為幾個(gè)方面:部署(含升級)可執(zhí)行文件與配置文件、監(jiān)控進(jìn)程狀態(tài)、管理服務(wù)進(jìn)程,合起來可稱為運(yùn)維 operation。
根據(jù)公司的規(guī)模和技術(shù)水平不同,分布式系統(tǒng)的運(yùn)維分為幾重境界,以下是我對各重境界的簡要描述。
境界1:全手工操作
這個(gè)大概是高校實(shí)驗(yàn)室的水平,分布式系統(tǒng)的規(guī)模不大,可能十來臺(tái)機(jī)器上下。分布式系統(tǒng)的實(shí)現(xiàn)者為在校學(xué)生。
系統(tǒng)完全是手工搭起來,host 的 IP 地址靜態(tài)配置。
部署:編譯之后手工把可執(zhí)行文件拷貝到各臺(tái)機(jī)器上,或者放到公用的 NFS 目錄下。配置文件也手工修改并拷貝到各臺(tái)機(jī)器上(或者放到每個(gè) Sudoku Solver 自己單獨(dú)的 NFS 目錄下)。
管理:手工啟動(dòng)進(jìn)程,手工在命令行指定配置文件的路徑。重啟進(jìn)程的時(shí)候需要登陸到 host 上并 kill 進(jìn)程。
升級:如果需要升級 Sudoku Solver,則需要手工登陸多臺(tái) hosts,可以拷貝新的可執(zhí)行文件覆蓋原來的,并重啟。
配置:Web Server 的配置文件里寫上 Sudoku Solver 的 ip:port。如果部署了新的 Sudoku Solver,多半要重啟 Web Server 才能發(fā)揮作用。
監(jiān)控:無。系統(tǒng)不是真實(shí)的商業(yè)應(yīng)用,僅僅用作學(xué)習(xí)研究,發(fā)現(xiàn)哪兒不對勁了就登陸到那臺(tái) host 上去看看,手工解決問題。
這個(gè)級別可算是“過家家”,系統(tǒng)時(shí)零時(shí)不靈,可以跑跑測試,發(fā)發(fā) paper。
境界2:使用零散的自動(dòng)化腳本和第三方組件
這大概是剛起步的公司的水平,系統(tǒng)已經(jīng)投入商業(yè)應(yīng)用。公司的開發(fā)重心放在實(shí)現(xiàn)核心業(yè)務(wù),添加新功能,暫時(shí)還顧不上高效的運(yùn)維,或許系統(tǒng)的運(yùn)維任務(wù)由開發(fā)人員或網(wǎng)管人員兼任。公司已經(jīng)有了基本的開發(fā)流程,代碼采用中心化的版本管理工具(比如 SVN),有比較正式的 QA sign-off 流程。
公司內(nèi)網(wǎng)有 DNS,可以把 hostname 解析為 IP 地址,host 的 IP 地址由 DHCP 配置。公司內(nèi)部的 host 的軟硬件配置比較統(tǒng)一,比如硬件都是 x86-64 平臺(tái),操作系統(tǒng)統(tǒng)一使用 Ubuntu 10.04 LTS,每天機(jī)器上安裝的 package 和第三方 library 也是完全一樣的(版本號也相同),這樣任何一個(gè)程序在任何一臺(tái) host 上都能啟動(dòng),不需要單獨(dú)的配置。
假設(shè)各臺(tái) host 已經(jīng)配置好了 ssh authentication key 或者 GSSAPI,不需要手工輸入密碼。如果要在 host1, host2, host3, host4 上運(yùn)行 md5sum 命令,看一下各臺(tái)機(jī)器上的 SudokuSolver 可執(zhí)行文件的內(nèi)容是否相同,可以在本機(jī)執(zhí)行:
for h in host1 host2 host3 host4; do ssh $h md5sum /path/to/SudokuSolver/version/bin/sudoku-solver ; done
公司的技術(shù)人員有能力配置使用 cron、at、logrotate、rrdtool 等標(biāo)準(zhǔn)的 linux 工具來將部分運(yùn)維任務(wù)自動(dòng)化。
部署:可執(zhí)行文件必須經(jīng)過 QA 簽署放行才能部署到生產(chǎn)環(huán)境(如有必要,QA 要簽署可執(zhí)行文件的 md5)。為了可靠性,可能不會(huì)把可執(zhí)行文件放到 NFS 上(如果 NFS 故障,整個(gè)系統(tǒng)就癱瘓了)。有可能采用 rsync 把可執(zhí)行文件拷貝到本機(jī)目錄(考慮到可執(zhí)行文件比較大,估計(jì)不適合直接放到版本管理庫里),并且用 md5sum 檢查拷貝之后的文件是否與源文件相同。部署可執(zhí)行文件這一步驟應(yīng)該可以用腳本自動(dòng)執(zhí)行(比方說 ssh $host rsync /path/to/source/on/nfs /path/to/local/copy/)。為了讓 C++ 可執(zhí)行文件拷到 host 上就能用,那么通常采用靜態(tài)鏈接,以避免 .so 版本不同造成故障。
Sudoku Solver 的配置文件會(huì)放到版本管理工具里,每個(gè) Solver instance 可能有自己的 branch,每次修改都必須入庫。程序啟動(dòng)的時(shí)候用的配置文件必須從 SVN 里 check-out,不能手工修改(減少人為錯(cuò)誤)。
管理:第一次啟動(dòng)進(jìn)程的時(shí)候,會(huì)從 SVN check-out 配置文件;以后重啟進(jìn)程的時(shí)候可以從本地 working copy 讀取配置文件(以避免 SVN 服務(wù)器故障對系統(tǒng)造成影響),只在改過配置文件之后才要求 svn update。服務(wù)進(jìn)程使用 daemon 方式管理 (/sbin/init 或 upright 工具),crash 之后會(huì)立刻自動(dòng)重啟(利用 respawn 功能)。服務(wù)進(jìn)程一般會(huì)隨 host 啟動(dòng)而啟動(dòng)(放到 /etc/init.d 里),如果要重啟 hostA 上的服務(wù)進(jìn)程,可以通過 ssh 遠(yuǎn)程操作(比如在本機(jī)運(yùn)行 ssh hostA /etc/init.d/sudoku-solver restart )。進(jìn)程管理是分散的,每臺(tái) host 運(yùn)行哪些 service 完全由本機(jī)是的 /etc/init.d 目錄決定。把一個(gè) service 從一臺(tái) host 遷移到另一臺(tái) host,需要登錄到這兩臺(tái) host 上去做一些手工配置。
升級:可執(zhí)行文件也有一套版本管理(不一定通過 SVN),發(fā)布新版本的時(shí)候嚴(yán)禁覆蓋已有的可執(zhí)行文件。比方說,現(xiàn)在運(yùn)行的:/path/to/SudokuSolver/1.0.0/bin/sudoku-solver
那么新版本的 Sudoku Solver 會(huì)發(fā)布到:/path/to/SudokuSolver/1.1.0/bin/sudoku-solver
這么做的原因是,對于 C++ 服務(wù)程序,如果在程序運(yùn)行的時(shí)候覆蓋了原有的可執(zhí)行文件,那么可能會(huì)在一段時(shí)間之后出現(xiàn) bus error,程序因 SIGBUS 而 crash。另外,如果程序發(fā)生 core dump,那么驗(yàn)尸 (post mortem) 的時(shí)候必須用“產(chǎn)生 core dump 的可執(zhí)行文件”配合 core 文件。如果覆蓋了原來的可執(zhí)行文件,post mortem 無法進(jìn)行。
配置:Web Server 的配置文件里寫上 Sudoku Solver 的 host:port (比 境界1 有所提高,這里依賴 DNS,通常 DNS 有一主一備,可靠性足夠高)。不過 Web Server 的配置文件和 Sudoku Solver 的配置文件是獨(dú)立的,如果新增了 Sudoku Solver 或者遷移了 host,除了修改 Sudoku Solver 的配置文件,還有修改所有用到它的 Web Server 的配置文件。這在系統(tǒng)規(guī)模比較小的時(shí)候尚且可行,系統(tǒng)規(guī)模一大,這種服務(wù)之間的依賴關(guān)系會(huì)變得隱晦。如果關(guān)閉了某個(gè)服務(wù)程序,可能一不小心造成其他組的某個(gè)服務(wù)失靈。如孟巖在《通過一個(gè)真實(shí)故事理解SOA監(jiān)管》舉的那個(gè)例子一樣。
監(jiān)控:公司會(huì)使用一些開源的監(jiān)控工具(以下以 Monit 為例)來監(jiān)控每臺(tái) host 的資源使用情況(內(nèi)存、CPU、磁盤空間、網(wǎng)絡(luò)帶寬等等)。必要的話可以寫一些插件,使之能監(jiān)控我們自己寫的服務(wù)程序 (Sudoku Solver)。但是這些監(jiān)控工具通常只是觀察者,它們與進(jìn)程管理工具是獨(dú)立的,只能看,不能動(dòng)。這些監(jiān)控工具有自己的配置文件,這些配置需要與 Sudoku Solver 的配置同步修改。Monit 可以管理進(jìn)程,但是它判斷服務(wù)進(jìn)程是否能正常工作是通過定時(shí)輪詢,不一定能立刻(幾秒鐘)發(fā)現(xiàn)問題。
在這個(gè)境界,分布式系統(tǒng)已經(jīng)基本可用了,但也有一些隱患。
配置零散
每個(gè)服務(wù)程序有自己獨(dú)立的配置,但是整個(gè)系統(tǒng)沒有全局的部署配置文件(比方說哪個(gè)服務(wù)程序應(yīng)該運(yùn)行在哪些 hosts 上)。
服務(wù)程序的配置文件和用到此服務(wù)的客戶端程序的配置是獨(dú)立的,如果把 Sudoku Solver 遷移到另一臺(tái) host,那么不僅要修改 Sudoku Solver 的配置,還要修改用到 Sudoku Solver 的 Web Server 的配置,以及監(jiān)控 Sudoku Solver 的 Monit 的配置。如果忘記修改其中一處,就會(huì)造成系統(tǒng)故障。
分布式系統(tǒng)中服務(wù)程序的依賴關(guān)系是個(gè)令人頭疼的問題,“依賴”還好辦(程序的作者知道我這個(gè)服務(wù)程序會(huì)依賴哪些其他服務(wù)),“被依賴”則比較棘手(如何才能知道停掉我這個(gè)程序會(huì)不會(huì)讓公司其他系統(tǒng)崩潰?)。這也從一個(gè)側(cè)面證明使用 TCP 協(xié)議作為唯一的 IPC 手段的必要性,如果采用 TCP 通訊,為了查出有哪些程序用到了我的 Sudoku Solver (假設(shè) listening port 是 9981),那么我只要運(yùn)行 NETstat -tpn |grep 9981 就能找到現(xiàn)在的客戶;或者讓 Sudoku Solver 自己打印 accept(2) log,連續(xù)檢查一周或這一個(gè)月就能知道有哪些程序用到了 Sudoku Solver。
進(jìn)程管理分散
如果 hostA 發(fā)生硬件故障,如何能快速地用一臺(tái)備用服務(wù)器硬件頂替它?能否先把它上面原來運(yùn)行的 Sudoku Solver 遷移到空閑的 hostB 上,然后通知 Web Server 用 hostB 上的 Sudoku Solver?“通知 Web Server”這一步要不要重啟 Web Server?
境界3:自制機(jī)群管理系統(tǒng),集中化配置
這可能是比較成熟的大公司的水平。
境界 2 中的分散式進(jìn)程管理已經(jīng)不能滿足業(yè)務(wù)靈活性方面的需求,公司開始整合現(xiàn)有的運(yùn)維工具,開發(fā)一套自己的機(jī)群管理軟件。我還沒有找到一個(gè)開源的符合我的要求的機(jī)群管理軟件,以下虛構(gòu)一套名為 Zurg (名字取自科幻電影《第五元素》,拼寫稍有不同;Zurg 也是《玩具總動(dòng)員》中的一個(gè)反派角色。)的分布式系統(tǒng)管理軟件。
Zurg 的架構(gòu)很簡單,典型的 master slave 結(jié)構(gòu),見陳碩在《多線程服務(wù)器的適用場合》中對“管理 Linux 服務(wù)器機(jī)群”的描述。
在《分布式系統(tǒng)的工程化開發(fā)方法》中談到了 Zurg 的功能需求:
到了這一境界,日常的管理運(yùn)維工作已經(jīng)不再需要反復(fù)執(zhí)行 ssh,常見任務(wù)都可以通過 Zurg 來完成。
部署:只需要向 master 發(fā)一條指令,master 會(huì)命令 slaves 從指定的地點(diǎn) rsync 新的可執(zhí)行文件到本地目錄。
進(jìn)程管理與監(jiān)控:Zurg 的主要功能就是進(jìn)程管理和監(jiān)控,比起一般的開源工具,Zurg 更具備一些優(yōu)勢。由于 Sudoku Solver 是由 Zurg Slave fork() 而得,那么當(dāng) Sudoku Solver crash 的時(shí)候,Zueg Slave 會(huì)立刻收到 SIGCHLD,從而能立刻向管理員報(bào)告狀態(tài)并重啟。這比 munit 的輪詢要迅速得多。(還可以在 fork() 之前做一些手腳,讓 Zueg Slave 能更方便地獲得 Sudoku Solver 的存活狀態(tài)。)
為了安全起見,Zurg Slave 在啟動(dòng)可執(zhí)行文件的時(shí)候可以驗(yàn)證其 md5,這樣避免錯(cuò)誤版本的服務(wù)程序運(yùn)行在生產(chǎn)環(huán)境。
Zurg Master 可以提供一個(gè) Web 頁面以供查看本機(jī)群內(nèi)各個(gè)服務(wù)程序是否正常運(yùn)行。并且提供一個(gè)接口(可以是 HTTP)讓我們能編寫腳本來控制 Zurg master。
升級:如果要主動(dòng)重啟 Sudoku Solver,可以向 Zurg master 發(fā)出指令,不需要用 ssh & kill。Zurg 會(huì)保存每臺(tái) host 上服務(wù)進(jìn)程的啟動(dòng)記錄,以便事后分析。如果用境界 2 中的手動(dòng) /etc/init.d 管理方式,需要到每臺(tái)機(jī)器上收集 log 才知道 Sudoku Solver 什么時(shí)候重啟過。
另外也可以單獨(dú)開發(fā) GUI 程序,運(yùn)行在運(yùn)維人員的桌面上,重啟多臺(tái) host 上的 Sudoku Solver 只需要點(diǎn)幾下鼠標(biāo)。
配置:零散的配置文件被集中的 Zurg 配置文件取代。
Zurg 配置文件會(huì)制定哪些 service 會(huì)在哪些 host 上運(yùn)行,Zurg Master 讀取配置文件,然后命令各個(gè) Zurg Slave 啟動(dòng)相應(yīng)的服務(wù)程序。比方說配置文件指定 Sudoku Solver 運(yùn)行在 host1、host2、host3 上,那么 Zurg 會(huì)通知在 host1、host2、host3 上的 Zurg Slave 啟動(dòng) Sudoku Solver。(當(dāng)然,每臺(tái) host 上的 Zurg Slave 需要由 /etc/init.d 啟動(dòng),其他的服務(wù)程序都由它負(fù)責(zé)啟動(dòng)。)
更重要的是,服務(wù)程序之間的依賴關(guān)系在 Zurg 配置文件里直接體現(xiàn)出來。比方說,在 Zurg 配置文件里指明 Web Server 依賴 Sudoku Solver,Web Server 的配置文件由 Zurg master 生成(可能會(huì)用到模板引擎,讀入一個(gè) Web Server 的配置模板),其中出現(xiàn)的 Sudoku Solver 的 host:port 由 Zurg master 自動(dòng)填上,這樣如果把 Sudoku Solver 從 hostA 遷移到 hostB,只需要改一處地方(Zurg 的配置),而 Sudoku Solver 和 Web Solver 的配置都由 Zurg master 自動(dòng)生成。這樣大大降低了犯錯(cuò)誤的機(jī)會(huì)。
到了這一境界,分布式系統(tǒng)日常管理已經(jīng)基本成熟,但在容錯(cuò)與負(fù)載均衡方面有較大的提升空間。
目前最大在障礙是 DNS,它限制了快速 Failover。比方說,如果 hostA 發(fā)生硬件故障,Zurg Master 固然可以在 hostB 上立刻啟動(dòng) Sudoku Solver,但是如何通知 Web Server 到 hostB 上享用服務(wù)呢?修改 DNS entry 的話(把 hostA 的域名解析到 hostB 的 IP),可能要好幾分鐘才能完成更新,因?yàn)?DNS 沒有推送機(jī)制。
如果思路受限制于 host:port,那么會(huì)采取一些看似高級,實(shí)則笨拙的高可用 (high availability) 解決方案。比方說在內(nèi)核里做做手腳,設(shè)法讓兩臺(tái)機(jī)器共享同一個(gè) IP,然后通過專門的心跳連線來控制哪臺(tái) host 對外提供服務(wù),哪臺(tái)是備用機(jī)。如果那臺(tái)“主機(jī)”發(fā)生故障,可以快速(幾秒鐘)切換到備用機(jī),因?yàn)?hostname 和 IP 地址是相同的,客戶端不用重新配置或重啟,只要重新連接 TCP 就能完成 failover。如果在錯(cuò)誤的道路上走得更遠(yuǎn)一點(diǎn),可能還會(huì)設(shè)法把 TCP 連接一同遷移到備用機(jī),這樣客戶端都不需要斷開并重連。
Load balance 也受限于 DNS。
如果發(fā)現(xiàn)現(xiàn)有的 4 個(gè) Sudoku Solver 不堪重負(fù),又部署了 4 臺(tái) Sudoku Solver,如何通知各個(gè) Web Server 把新的 Sudoku Solver 加到連接池里?
有一些 ad hoc 的手段,比方說每個(gè) Web Server 有一個(gè)管理接口,可以透過這個(gè)接口向它動(dòng)態(tài)地增減 Sudoku Solver 的地址。借助這個(gè)管理接口,我們也可以做一些計(jì)劃中的聯(lián)機(jī)遷移。比方說要主動(dòng)把某個(gè) Sudoku Solver 從 hostA 遷移到 hostB,我們可以先在 hostB 上啟動(dòng) Sudoku Solver,然后透過 Web Server 的管理接口把 hostB:9981 添加到 Web Server 的連接池中,再把 hostA:9981 從連接池中刪掉,最后停掉 hostA 上的 Sudoku Solver。這對計(jì)劃中的 Sudoku Solver 升級是可行的,能做到避免中斷 Web Server 服務(wù)。對于 failover,這種做法似乎稍顯不夠方便,因?yàn)橐?Zurg Master 理解 Web Server 的管理接口,會(huì)給系統(tǒng)帶來循環(huán)依賴。(正常情況下,Zurg Master 不應(yīng)該知道/訪問它管理的服務(wù)程序的接口細(xì)節(jié),這樣 Sudoku Solver 升級的時(shí)候不用升級 Zurg Master。)
這種做法要求 Web Server 在開發(fā)的時(shí)候留下適當(dāng)?shù)木S修探查通道,見陳碩《構(gòu)建易于維護(hù)的分布式程序》中的推薦做法。
另外一種 ad hoc 的手段,每個(gè) Sudoku Solver 在啟動(dòng)的時(shí)候自己主動(dòng)往某個(gè)數(shù)據(jù)庫表里 insert 或 update 本程序的 host:port。Web Server 的配置里寫的不是 host:port,而是一條 SELECT 語句,用于找出它依賴的 Sudoku Solver 的 host:port,Web Server 還可以通過數(shù)據(jù)庫觸發(fā)器來及時(shí)獲知 Sudoku Solver address list 的變化。這樣增加或減少 Sudoku Server 的話,Web Server 幾乎可以立刻應(yīng)對,也不需要透過管理接口來手工增減 Sudoku Solver 地址。數(shù)據(jù)庫在這里扮演了 naming service 的角色,它的可用性直接影響了整個(gè)系統(tǒng)的可用性。
境界 3 是黎明前的黑暗,只要統(tǒng)一引入 naming service,拋開 DNS,容錯(cuò)和負(fù)載均衡的問題迎刃而解。
境界4:機(jī)群管理與 naming service 結(jié)合
這是業(yè)內(nèi)領(lǐng)先的公司的水平。
前面分析到,使用 Zurg 機(jī)群管理軟件能大大簡化分布式系統(tǒng)的日常運(yùn)維,但是它也有很大的缺陷——不能實(shí)現(xiàn)快速 failover。如果系統(tǒng)規(guī)模大到一定程度,機(jī)器出故障的頻率會(huì)顯著增加,這時(shí)候自動(dòng)化的快速 failover 是必備的,否則運(yùn)維人員疲于奔命救火。
實(shí)現(xiàn)簡單而快速的 failover 不需要特殊的編程技巧,也不需要對 kernel 動(dòng)手腳,只要拋棄傳統(tǒng)的 DNS 觀念,擺脫 host:port 的束縛,采用為分布式系統(tǒng)特制的 naming service 代替 DNS 即可。
naming service 的功能是把一個(gè) service_name 解析成 list of ip:port。比方說,查詢 "sudoku_solver",返回 host1:9981、host2:9981、host3:9981。
naming service 與 DNS 最大的不同在于它能把新的地址信息推送給客戶端。比方說,Web Server 訂閱了 "sudoku_solver",每當(dāng) sudoku_solver 發(fā)生變化,Web Server 就會(huì)立刻收到更新。Web Server 不需要輪詢,而是等候通知。
naming service 誰負(fù)責(zé)更新?
在境界 2 中,Sudoku Solver 會(huì)自己主動(dòng)去 naming server 注冊。到了境界 3,由于 Sudoku Solver 是有 Zurg 負(fù)責(zé)啟動(dòng),那么 Zurg 知道 Sudoku Solver 運(yùn)行在哪些 hosts 上,它會(huì)主動(dòng)更新 naming service,不需要 Sudoku Solver 自己動(dòng)手。
naming service 的可用性(availability)和一致性如何保證?
毫無疑問,一旦采用這種方案,naming service 是系統(tǒng)正常運(yùn)轉(zhuǎn)的關(guān)鍵,它的可用性決定了系統(tǒng)的可用性。naming service 絕對不能只 run 在一臺(tái)服務(wù)器上,為了可靠性,應(yīng)該用一組(通常是 5 臺(tái))服務(wù)器同時(shí)提供服務(wù),當(dāng)然,這需要解決一致性問題。目前實(shí)現(xiàn)高可用 naming service 的公認(rèn)辦法是 Paxos 算法,也有了一些開源的實(shí)現(xiàn)(ZooKeeper、KeySpace、Doozer)。
對程序設(shè)計(jì)的影響?
如果公司的網(wǎng)絡(luò)庫在設(shè)計(jì)的時(shí)候就考慮了 naming service,那么對程序設(shè)計(jì)來說是透明的。配置文件里寫的不再是 host:port,而是 service_name,交給網(wǎng)絡(luò)庫去解析成 ip:port 地址列表。
為什么 muduo 網(wǎng)絡(luò)庫沒有封裝 DNS 解析?
一方面因?yàn)?gethostbyname() 和 getaddrinfo() 做 DNS 解析是阻塞的,我一時(shí)沒有時(shí)間寫一個(gè)非阻塞的 DNS 庫;另一方面,因?yàn)樵诖笠?guī)模分布式系統(tǒng)中 DNS 的作用不大,我寧愿花時(shí)間實(shí)現(xiàn)一個(gè) naming service,并且為它編寫 name resolve library。
在境界 3 中,每個(gè)項(xiàng)目組有自己的 hosts,只運(yùn)行本項(xiàng)目中的服務(wù)程序,每個(gè)服務(wù)程序的 TCP 端口可以靜態(tài)分配(比如 Sudoku Solver 固定使用 9981 端口),不擔(dān)心端口沖突。如果公司規(guī)模繼續(xù)擴(kuò)大,遲早會(huì)把 16-bit 的 port 命名空間用完,這時(shí)候給新項(xiàng)目分配端口號將成為問題。
到了境界 4,這一限制將被打破,服務(wù)程序可以 run 在公司內(nèi)任何一臺(tái) host 上,也不用擔(dān)心端口沖突,因?yàn)?Zurg 會(huì)選擇當(dāng)前 host 的空閑端口來啟動(dòng) Sudoku Solver,并且把選中的端口保存在 naming service 中。這樣一來,TCP port 也實(shí)現(xiàn)了動(dòng)態(tài)配置,Web Server 完全能自動(dòng)適應(yīng) run 在不同 port 的 Sudoku Solver。
(待續(xù),下一篇我打算談?wù)劮植际较到y(tǒng)中心跳協(xié)議的設(shè)計(jì)。)
it知識(shí)庫:分布式系統(tǒng)部署、監(jiān)控與進(jìn)程管理的幾重境界,轉(zhuǎn)載需保留來源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請第一時(shí)間聯(lián)系我們修改或刪除,多謝。