近日,谷歌工程師Rachel Potvin和Josh Levenberg在《美國計(jì)算機(jī)學(xué)會(huì)通訊》上發(fā)表了一篇論文,介紹谷歌為什么采用一個(gè)定制的大型單體共享庫。該庫有一個(gè)集中式源代碼控制系統(tǒng)管理。谷歌采用該方法已達(dá)16年之久。如今,谷歌大部分的資產(chǎn)都存儲(chǔ)在這個(gè)單體共享庫中。
谷歌的軟件開發(fā)人員不斷增加,其代碼庫也成倍地增大,管理代碼庫的技術(shù)也在相應(yīng)地發(fā)展。為管理這個(gè)谷歌95%的軟件開發(fā)人員都在使用的代碼庫,谷歌開發(fā)了自己的版本控制系統(tǒng)。該集中式系統(tǒng)是谷歌開發(fā)工作流的基礎(chǔ),支撐著谷歌“基于主干的開發(fā)策略”,確保了代碼庫的健康,包括靜態(tài)分析、代碼清理和簡(jiǎn)潔的代碼評(píng)審。這個(gè)代碼庫包含大約10億個(gè)文件,86TB的數(shù)據(jù),其中包含大約20億行代碼,18年間大約產(chǎn)生了3500萬次提交。通常,一個(gè)工作日就會(huì)有16000次提交,以及24000個(gè)來自自動(dòng)化系統(tǒng)的提交。此外,該庫每天還要處理數(shù)以10億計(jì)的文件讀取請(qǐng)求以及每秒500000次查詢。
管理這樣一個(gè)大規(guī)模的庫及其相關(guān)活動(dòng)是一項(xiàng)巨大的挑戰(zhàn)。雖然經(jīng)過了多年的試驗(yàn),但谷歌還是沒能找到一個(gè)商業(yè)或者開源版本控制系統(tǒng)來管理這種規(guī)模的單體代碼庫。于是,谷歌基于標(biāo)準(zhǔn)的基礎(chǔ)設(shè)施(最初是BigTable,現(xiàn)在是Spanner)構(gòu)建了Piper來存儲(chǔ)和管理它。Piper分布在谷歌全球范圍內(nèi)的10多個(gè)數(shù)據(jù)中心里,基于Paxos算法來保證副本之間的一致性。該架構(gòu)提供了很高的冗余水平,并幫助谷歌的開發(fā)人員優(yōu)化網(wǎng)絡(luò)延遲,而不管他們工作地點(diǎn)在哪里。該系統(tǒng)的工作流程如下:
大多數(shù)開發(fā)人員都是使用Clients in the Cloud(CitC)訪問Piper。該系統(tǒng)包含一個(gè)基于云的存儲(chǔ)后端和一個(gè)Linux特有的文件系統(tǒng)FUSE。CitC 支持代碼瀏覽和常見的Unix工具。開發(fā)人員可以瀏覽和編輯Piper庫中的文件,但只有修改過的代碼會(huì)存儲(chǔ)在用戶的工作區(qū)里。也就是說,CitC工作區(qū)通常只占用很小的存儲(chǔ)空間。
谷歌在這個(gè)巨大的Piper源代碼庫上踐行著“基于主干的開發(fā)”策略。大多數(shù)Piper用戶都工作在“主干”的唯一最新副本上。代碼庫的修改順序是一個(gè)串行序列。提交完成后,其他所有的開發(fā)人員立即就可以看到和使用新的代碼。Piper的所有用戶都使用一個(gè)一致的代碼庫視圖,這可以避免代碼合并的痛苦,是單體大型代碼庫能夠具備如下優(yōu)勢(shì)的關(guān)鍵所在:
統(tǒng)一版本控制廣泛地代碼共享和重用簡(jiǎn)化依賴管理,避免菱形依賴原子修改大規(guī)模重構(gòu)跨團(tuán)隊(duì)協(xié)作靈活的團(tuán)隊(duì)邊界和代碼所有權(quán)代碼可見性以及清晰的樹形結(jié)構(gòu)提供了隱含的團(tuán)隊(duì)命名空間
總之,將所有源代碼存儲(chǔ)在一個(gè)公用的版本控制庫中,讓代碼庫維護(hù)人員能夠有效地分析和修改源代碼。類似Refaster和ClangMR這樣的工具可以利用谷歌代碼庫的單體視圖執(zhí)行高級(jí)的代碼轉(zhuǎn)換。單體代碼庫包含了所有依賴信息。舊的API可以很有把握地刪除,因?yàn)榭梢宰C明,所有的調(diào)用者都已經(jīng)遷移到了新API。
雖然有許多好處,但構(gòu)建這樣一個(gè)龐大的單體代碼庫也有幾個(gè)方面的問題需要權(quán)衡。
工具投入
單體代碼庫在許多方面簡(jiǎn)化了工具,但它們需要能夠擴(kuò)展并適用于代碼庫的規(guī)模。例如,谷歌開發(fā)了一個(gè)Eclipse IDE插件,以便能夠在該IDE中操作這個(gè)巨大的代碼庫。谷歌的代碼索引系統(tǒng)為靜態(tài)分析、代碼瀏覽工具交叉引用等提供了支持。這些工具都需要不斷的投入以適應(yīng)代碼庫的不斷增長(zhǎng)。除了構(gòu)建和維護(hù)這些可擴(kuò)展的工具之外,谷歌還需要承擔(dān)運(yùn)行這些系統(tǒng)的成本,有些是計(jì)算密集型的。
代碼庫復(fù)雜性
雖然單體模型讓代碼結(jié)構(gòu)更容易理解,但卻讓代碼發(fā)現(xiàn)變得更困難。隨著代碼庫規(guī)模的增長(zhǎng),像grep這樣的標(biāo)準(zhǔn)工具就無法使用了。開發(fā)人員需要能夠查看代碼庫,找到相關(guān)程序庫,并看看如何使用它們以及誰編寫了它們。這就需要有代碼搜索和代碼瀏覽工具。此外,由于添加依賴變得簡(jiǎn)單,所以開發(fā)團(tuán)隊(duì)對(duì)依賴圖的考慮就比較少,這增加了代碼清理時(shí)犯錯(cuò)的可能性。因此,需要有依賴重構(gòu)和代碼清理輔助工具。
代碼健康
谷歌在代碼健康上投入了大量的精力。例如,專用工具可以自動(dòng)檢測(cè)和刪除無用代碼、分派代碼評(píng)審任務(wù)等。
另外,隨著分布式版本控制系統(tǒng)(如Git)的流行,谷歌也考慮過是否用Git取代Piper作為基本的版本控制系統(tǒng)。由于外部合作伙伴和開源協(xié)作方面的原因,谷歌的Android和Chrome團(tuán)隊(duì)就使用Git。Git社區(qū)強(qiáng)烈建議使用更多更小的代碼庫,而且Git克隆操作會(huì)把所有內(nèi)容都復(fù)制到本地機(jī)器上。因此,如果要遷移到Git,谷歌就需要把那個(gè)巨大的代碼庫分割成成千上萬的小代碼庫。對(duì)谷歌的開發(fā)人員而言,這意味著文化和工作流程的變革。鑒于單體代碼庫所帶來的好處,他們放棄了這種遷移。
這篇論文在Hacker News上引發(fā)了激烈的討論。網(wǎng)友rzimmerman認(rèn)為,如果沒有人致力于維護(hù)構(gòu)建環(huán)境、自動(dòng)化測(cè)試和開發(fā)環(huán)境,那么大型單體代碼庫的許多好處將不復(fù)存在。如果運(yùn)行測(cè)試、生成構(gòu)建需要幾個(gè)小時(shí)才能完成,就會(huì)嚴(yán)重降低團(tuán)隊(duì)的效率。但wtbob認(rèn)為,多代碼庫只是模糊了單體庫中明確需要完成的工作,但并沒有減少相關(guān)工作。例如,在單體庫中,一項(xiàng)破壞性修改所產(chǎn)生的全部影響立即就會(huì)顯現(xiàn)出來;但在多代碼庫的情況下,有一些影響要在很長(zhǎng)時(shí)間之后才能發(fā)現(xiàn)。
網(wǎng)友hpaavola認(rèn)為單代碼庫確實(shí)便于處理依賴問題。據(jù)他介紹,他們的產(chǎn)品族包含多個(gè)產(chǎn)品。每個(gè)產(chǎn)品都有自己的代碼庫,每個(gè)代碼庫又有自己的功能測(cè)試。每當(dāng)測(cè)試自動(dòng)化核心功能發(fā)生變化,他就需要為每個(gè)產(chǎn)品的代碼庫生成一個(gè)分支,并逐個(gè)進(jìn)行測(cè)試,看修改是否破壞了向后兼容性。如果破壞了,他就需要通過向多個(gè)團(tuán)隊(duì)提交pull request修復(fù)測(cè)試。這是一個(gè)痛苦的過程。在單代碼庫的情況下,修改要容易得多,只需要生成一個(gè)分支,提交一個(gè)pull request。
上述觀點(diǎn)只是其中部分頗具代表性的觀點(diǎn)。網(wǎng)友們還就一些具體的工作流程和實(shí)踐進(jìn)行了討論,感興趣的讀者可以移步Hacker News。