作者 | Andrew Israel
編譯 | 王瑞平
Rust作為長(zhǎng)期以來(lái)被看好的網(wǎng)絡(luò)開發(fā)語(yǔ)言,更注重技術(shù)的穩(wěn)定性,不掉鏈子,能夠?qū)⒃O(shè)備的性能發(fā)揮到極致,更講究精致。
相對(duì)于其它類型的語(yǔ)言來(lái)講,Rust是新成員。最早由Mozilla于2014年4月9日發(fā)布,是一款高級(jí)通用語(yǔ)言,能夠兼顧開發(fā)與執(zhí)行效率。
雖然Rust并不是一個(gè)專屬的網(wǎng)絡(luò)應(yīng)用開發(fā)語(yǔ)言,但是非常適合網(wǎng)絡(luò)開發(fā)。編譯器能就安全穩(wěn)定方面的問(wèn)題作出提醒。這使其具備了后端網(wǎng)絡(luò)開發(fā)的獨(dú)特優(yōu)勢(shì)。
我曾在《用Rust創(chuàng)建一家初創(chuàng)公司》一書中提到:“初創(chuàng)公司應(yīng)優(yōu)先考慮開發(fā)人員所帶來(lái)的生產(chǎn)力而并非其能力。”對(duì)于一家創(chuàng)業(yè)公司創(chuàng)始人來(lái)講,這種觀點(diǎn)是明智的。也正因?yàn)槿绱?,我喜歡使用Rust,并雇傭了同樣喜歡使用Rust的開發(fā)人員。
我必須提醒:如果你的團(tuán)隊(duì)中沒(méi)有其他人會(huì)使用Rust,那么,教授所有同事使用Rust的成本將會(huì)很高。他們可能需要一段時(shí)間才能游刃有余地使用Rust,在此期間,你需要指導(dǎo)他們,工作效率會(huì)因此下降。明智的選擇是使用團(tuán)隊(duì)其他人都知道的語(yǔ)言,除非你真正需要使用Rust。
幸運(yùn)的是,我的隊(duì)友已經(jīng)了解并喜歡上了使用Rust,并熟知如何讓代碼生成工具(如,Serde和Diesel)最大效能地發(fā)揮作用,以成為更好的Rust程序員。
1、用Rust語(yǔ)言建立數(shù)據(jù)泄露防護(hù)系統(tǒng)
我的團(tuán)隊(duì)為Cloudflare建立了數(shù)據(jù)泄露防護(hù)系統(tǒng)。該系統(tǒng)通過(guò)對(duì)網(wǎng)絡(luò)流量進(jìn)行“掃描”確保私人數(shù)據(jù)沒(méi)有被泄露。例如,它可以檢測(cè)并阻止黑客從你的數(shù)據(jù)庫(kù)中上傳數(shù)百萬(wàn)個(gè)信用卡號(hào)碼到pastebin.org,或者阻止某人將帶有特定Office標(biāo)簽的Word文檔發(fā)送到你的yahoo.com電子郵件。
實(shí)際上,我們可以將掃描網(wǎng)站以防止數(shù)據(jù)丟失的服務(wù)想象為數(shù)據(jù)泄露防護(hù)系統(tǒng)掃描儀。在此過(guò)程中,系統(tǒng)可能同時(shí)代理很多http請(qǐng)求,對(duì)性能敏感。
我們不希望用戶在打開數(shù)據(jù)泄露防護(hù)系統(tǒng)時(shí),網(wǎng)頁(yè)瀏覽速度變慢,并因此提供了兩種構(gòu)建后端API可供選擇的語(yǔ)言:Rust和Go。
無(wú)論使用哪種語(yǔ)言,構(gòu)建出的后端API必須能夠與數(shù)據(jù)泄露防護(hù)系統(tǒng)進(jìn)行互操作,并能夠共享一系列類型,如:表達(dá)用戶配置等。API服務(wù)器將用戶配置序列化為JSON,數(shù)據(jù)泄露防護(hù)系統(tǒng)將在需要掃描請(qǐng)求時(shí)反序列化該JSON。
在實(shí)際操作過(guò)程中,我更傾向于用Rust語(yǔ)言編寫所有序列化和反序列化程序。此外,我個(gè)人比較傾向于在系統(tǒng)的不同部分之間共享代碼,針對(duì)性能關(guān)鍵型服務(wù)和非性能敏感型服務(wù)使用Rust可以大大簡(jiǎn)化整個(gè)代碼庫(kù)。
2、用Rust構(gòu)建數(shù)據(jù)庫(kù)
雖然Rust在構(gòu)建數(shù)據(jù)庫(kù)方面并不出色,但我還是認(rèn)為它在此方面性能優(yōu)良。
就拿Rust語(yǔ)言中的Diesel框架來(lái)說(shuō),它能夠從SQL數(shù)據(jù)庫(kù)語(yǔ)言之中遷移生成類型化SQL模式,從而生成所有SQL查詢。此外,當(dāng)更新SQL模式時(shí),Diesel將重新生成適當(dāng)?shù)腞ust模型。
實(shí)際上,在Rust類型系統(tǒng)中構(gòu)建SQL模型會(huì)導(dǎo)致一系列問(wèn)題,包括:錯(cuò)誤消息超過(guò)60行、毫無(wú)意義的錯(cuò)誤信息、很難將公共代碼分解為共享函數(shù)等。
但總的來(lái)說(shuō),如果你的應(yīng)用程序在很大程度上依賴于數(shù)據(jù)庫(kù)的許多功能,我認(rèn)為有必要確保你的數(shù)據(jù)庫(kù)查詢獲得了正確的檢查類型。
數(shù)據(jù)庫(kù)查詢不是API后端中可選的額外內(nèi)容,它們幾乎是你的整個(gè)代碼庫(kù)。所以確保它們正確是值得的。
3、用Rust進(jìn)行業(yè)務(wù)建模
運(yùn)用Rust語(yǔ)言中的Diesel和Serde框架,你可以在API中生成幾乎所有重要的代碼(讀取請(qǐng)求、執(zhí)行數(shù)據(jù)庫(kù)查詢和編寫響應(yīng)),從而使你有更多的時(shí)間來(lái)編寫業(yè)務(wù)程序、發(fā)布特性并進(jìn)行業(yè)務(wù)建模。
重要的是,存儲(chǔ)用戶配置的后端API能夠在軟件中正確地模擬現(xiàn)實(shí)世界。如果用戶想在軟件中模擬辦公室布局,類型系統(tǒng)就能夠直接對(duì)辦公室建模,而不必讓用戶推送無(wú)效配置。
用戶往往希望在編譯時(shí)而不是在運(yùn)行時(shí)檢測(cè)到無(wú)效的配置,從而盡量減少測(cè)試和錯(cuò)誤代碼。例如,用戶的辦公室不可能同時(shí)位于兩個(gè)時(shí)區(qū)。那么,你的軟件模型就不應(yīng)該能夠表示具有兩個(gè)時(shí)區(qū)的辦公室。
對(duì)了,Rust有兩個(gè)特性可以幫助你準(zhǔn)確地進(jìn)行業(yè)務(wù)建模:枚舉和不可克隆類型。
重點(diǎn)說(shuō)下Rust的枚舉特性。它還可以被稱為“和類型”、“標(biāo)記聯(lián)合”、“代數(shù)數(shù)據(jù)類型”或“帶有關(guān)聯(lián)值的枚舉”。這取決于你使用的語(yǔ)言。我個(gè)人比較喜歡求和類型,iPhone開發(fā)者可以在Swift中使用。
準(zhǔn)確地進(jìn)行業(yè)務(wù)建模是我在構(gòu)建高級(jí)API中非常關(guān)心的事情,正確性至關(guān)重要。如果需要確保我的軟件模型準(zhǔn)確表現(xiàn)出現(xiàn)實(shí)世界,Rust比Go更好用。
現(xiàn)在談?wù)凴ust的“不可克隆類型”特性。在實(shí)際操作過(guò)程中,如果其中一個(gè)IP是“不健康”的,并斷開了Cloudflare連接,那么,Cloudflare需要避免重復(fù)使用這些IP,并使用一些以前沒(méi)有用過(guò)的IP。
應(yīng)確保每個(gè)IP都有三種狀態(tài):正在使用、未使用和以前使用過(guò)但現(xiàn)在“不健康”。這些IP中的每一個(gè)都可以分配給四個(gè)長(zhǎng)時(shí)間TCP連接中的一個(gè)。
這聽(tīng)起來(lái)像是一個(gè)很容易解決的問(wèn)題,但在實(shí)際操作過(guò)程中很難對(duì)“每個(gè)IP地址最多只能分配給一個(gè)連接”的想法進(jìn)行建模。我必須編寫大量單元測(cè)試程序,以找到兩個(gè)不同的連接獲取相同IP地址的邊緣情況。
Rust可以很容易地確保特定值只在一個(gè)地方使用。這需要確保使用該值的函數(shù)都必須引用它,或者確保你的類型沒(méi)有被強(qiáng)制Clone,并保證使用它的函數(shù)擁有該值的全部所有權(quán)。這樣,該值將能夠在移動(dòng)時(shí)移動(dòng)到函數(shù)中,函數(shù)可以在完成時(shí)返回該值。
所以,如果我想在Rust中實(shí)現(xiàn)上述操作系統(tǒng),只需要保留我的10個(gè)IP地址的HashSet,也要確保IP沒(méi)有派生克隆出新類型。因此,Rust的“不可克隆類型”特性至關(guān)重要。
4、Rust的可靠性
對(duì)于你的初創(chuàng)公司來(lái)說(shuō),系統(tǒng)的可靠性很重要。我們提供的Rust后端服務(wù)的優(yōu)點(diǎn)是它從不崩潰。在實(shí)踐中,Rust通常有更好的方法處理不同的選項(xiàng)。
而這種可靠性肯定會(huì)帶來(lái)開發(fā)人員的額外開銷,比如,考慮如何正確地匹配所有的Result和Option值。但對(duì)于許多領(lǐng)域來(lái)說(shuō),這種付出是有意義的。
值得注意的是,Rust不傾向于使用太多內(nèi)存(如TCP連接或文件描述符)。因?yàn)楫?dāng)函數(shù)終止時(shí),所有內(nèi)容都會(huì)被刪除和清理。
在實(shí)際應(yīng)用過(guò)程中,性能問(wèn)題最終會(huì)變成可靠性問(wèn)題。如果你的服務(wù)泄漏內(nèi)存的時(shí)間足夠長(zhǎng),或者攝入了足夠多的數(shù)據(jù),那么性能存在的瓶頸可能會(huì)導(dǎo)致服務(wù)器宕機(jī)。
5、是否應(yīng)該選擇Rust
Rust作為高級(jí)系統(tǒng)編程語(yǔ)言做出了令人滿意的成績(jī)。當(dāng)你在網(wǎng)絡(luò)開發(fā)時(shí),它可以通過(guò)Serde和Diesel節(jié)省開發(fā)者時(shí)間。神奇的是,雖然類型系統(tǒng)簡(jiǎn)化了業(yè)務(wù)建模過(guò)程,但是服務(wù)質(zhì)量卻不會(huì)因此下降。
對(duì)于Rust語(yǔ)言的使用效果評(píng)價(jià)并不是絕對(duì)的,需要根據(jù)不用的情況進(jìn)行判斷。如果你的團(tuán)隊(duì)沒(méi)有過(guò)多的Rust使用經(jīng)驗(yàn),在網(wǎng)絡(luò)開發(fā)時(shí)使用Rust可能會(huì)帶來(lái)非常糟糕的結(jié)果。Rust的使用難度極高,你應(yīng)該根據(jù)具體情況引導(dǎo)團(tuán)隊(duì)使用熟知的語(yǔ)言。
公司會(huì)根據(jù)不同的情況使用不同的語(yǔ)言。在Cloudflare, 我們執(zhí)行大多數(shù)對(duì)性能敏感的服務(wù)過(guò)程中使用Rust,執(zhí)行對(duì)性能寬松的服務(wù)(如,API后端)過(guò)程中則使用Go。我公司的團(tuán)隊(duì)過(guò)去使用Go語(yǔ)言進(jìn)行后端開發(fā),由于上文提到的原因逐漸遷移至Rust語(yǔ)言。
對(duì)于使用Rust語(yǔ)言的不同權(quán)衡并非對(duì)每個(gè)團(tuán)隊(duì)都有意義。這主要是由于學(xué)習(xí)Rust和在Rust中重寫核心業(yè)務(wù)庫(kù)需要耗費(fèi)巨大成本。即便如此,仍有越來(lái)越多團(tuán)隊(duì)愿意考慮使用Rust作為其在后端開發(fā)過(guò)程中使用的語(yǔ)言。
總之,不同公司應(yīng)該根據(jù)自身的情況使用熟知的語(yǔ)言完成工作。如果你的團(tuán)隊(duì)已經(jīng)熟知Rust,那么,在完成高級(jí)項(xiàng)目過(guò)程中使用它絕對(duì)是明智的。
參考鏈接:??https://blog.adamchalmers.com/why-rust-on-backend/??