談談編譯和運行
2018-07-20 來源:編程學習網(wǎng)


[作者按] 今天 hacker news 爆炸性的新聞是我們敬愛的葛老頭:Andy Grove 去了。70后,80后大多聽過這個響當當?shù)拿,也聽過(或者讀過)『只有偏執(zhí)狂才能生存』這本書。在90年代,葛魯夫和蓋茨一樣,基本上等同于他們所締造的王國,以及他們創(chuàng)立的 Wintel 聯(lián)盟。如今90后創(chuàng)業(yè)者們所津津樂道的所謂創(chuàng)業(yè)大師們總結的:專注,獨特,10倍速因子等等其實是拾了老爺子牙慧的。老爺子還有一本據(jù)說更好的書:High output management ,Ben(a16z 的合伙人)對其大為推崇。后知后覺的我沒有讀過,趕緊去圖書館 hold 了一本。去年,老爺子參加了 Ben 主持的一個頒獎會,看他幾乎站都站不穩(wěn),話都快說不清楚的老態(tài)讓我心酸,接著,聽到他顫巍巍說出這句話,我?guī)缀醵家獪I奔了:
Let's remember that millions of young people who have had the misfortune of being born in the wrong national boundaries are going through all the horrors that Ben described. I made it. Let's try in a little way to help them make it.
老爺子此時已經(jīng)病入膏肓,無藥可醫(yī),但依然心系那些和他有著類似磨難的年輕人。這便是范文正所謂的「不以物喜,不以己悲,居廟堂之高則憂其民;處江湖之遠則憂其君」的寫照。
葛老師一路走好。
。。。
言歸正傳。本來今天要講講 API 系統(tǒng)的配置和 CLI 等子系統(tǒng),但是很多同學對我上一篇文章 再談 API 的撰寫 - 架構 中所述的「編譯時」和「運行時」有不少困惑。這篇文章先講講這兩個概念。
在 上一篇 文章里,我講到:
通過這樣一個接口,我們把 API 系統(tǒng)區(qū)隔為「編譯時」和「運行時」。這個接口寫出來的 API,更像是一個等待編譯的源文件。在 API 系統(tǒng)啟動的時候,會經(jīng)歷一個「編譯」的過程,把所有的 route 匯總起來,生成 restify 認識的路由形式,同時,收集里面的各種信息(比如 validator,authentication),供框架的各個 middleware 使用。
「編譯」(compile)是軟件系統(tǒng)的一個非常非常重要的概念;很可惜,在 python / ruby / javascript 等解釋型語言大行其道的當下,很多人已經(jīng)不知道編譯為何物。當然,即便你使用 c / go / java 等編譯型語言,有多少人又真正清楚「編譯」究竟是個什么過程呢?
在 wikipedia,compile / compiler 的解釋如下:
A compiler is a computer program (or a set of programs) that transforms source code written in a programming language (the source language) into another computer language (the target language), with the latter often having a binary form known as object code.[1] The most common reason for converting source code is to create an executable program.
所以 compile 實際上是一種 transformation(我們又見到這個詞了):它把某個數(shù)據(jù)(如果你認為源代碼也是一種數(shù)據(jù)的話)從一種格式轉換成另外一種格式。在編譯型語言里,這種轉換是為了生成機器碼(如 c / go),或者 byte code(java / c#),方便機器執(zhí)行(byte code 會進一步以 JIT 的方式 compile 成機器碼)。
(題外話:其實解釋型語言也是有一個 JIT 「編譯」的過程;現(xiàn)在純粹的,完全在運行時一句句解釋執(zhí)行的語言,只能生存在象牙塔里)
有了這樣一層(indirection)編譯的過程,源數(shù)據(jù)和目標數(shù)據(jù)就被分離開,可以做很多事情,比如 wikipeidia 上說的 compiler 的一大功效:
Compilers enabled the development of programs that are machine-independent.
這里面,這個 machine-independent 可以根據(jù)你的需要被換成 framework-independent,甚至 language-independent。
那么,一份源代碼除了可以生成目標代碼(主產(chǎn)品)外,還能有什么副產(chǎn)品?我們以 java 為例:
-
如果你的注釋遵循 javadoc,那么從代碼里可以生產(chǎn)出來漂亮的文檔(SDK)。
-
facebook/infer 可以對你的代碼做詳細的 static analysis。
-
jacoco 可以根據(jù)源碼和 test case 生成 coverage report。
-
...
這些副產(chǎn)品帶啦的好處是顯而易見的:我們不用為了一些特定的目的而做一些額外的事情。
回到我們這幾天說的 API 系統(tǒng)。我提到了這樣的一個接口:
你可以將其看做是一段聲明 API 的代碼,但我更愿意將其看做是一段描述 API 的數(shù)據(jù)。這個數(shù)據(jù)有:
-
method:API 使用何種 http 方法調(diào)用。
-
path:API 使用什么樣的 endpoint。
-
description:API 的文檔。
-
validators:如果要驗證 API 的輸入數(shù)據(jù),如何驗證。
-
action:API 具體做些什么事情。
-
flags:API 有哪些屬性(需不需要驗證,支不支持某些特定的操作等)。
如果你以數(shù)據(jù)的眼光看待這段代碼,那么,每一個 route() 的聲明都可以被聚合起來,放到一個數(shù)組里。事實上,route 的實現(xiàn)就是如此:
每當用戶撰寫一個 route 的時候,我們實際上在往一個 list 里 push 這個 route 的數(shù)據(jù)。這個 list 究竟怎么用,是生成 restify 的 route,還是生成 hapi 的 route,我們在編譯時再具體決定。這便是 framework-independent。
那么,什么是編譯時,什么又是運行時呢?
就這么簡單。app.compile() 把放在 route list 里面的數(shù)據(jù)轉換成 restify 的 route,而 app.run() 開始進行網(wǎng)絡監(jiān)聽。很多同學看到這里會想,有沒有搞錯,我還以為是什么高深的東西呢,這代碼我也會寫啊。的確,這里沒有任何高深的東西。然而,關鍵的是你會不會想到把一段代碼的運行分解成:compile() 和run() 兩個階段。只有你這么去想了,你才會反過來考慮你的 API 的代碼能不能退一步,用一個數(shù)據(jù)結構封裝,你才會想這個數(shù)據(jù)結構該如何設置,你才會把 route() 的實現(xiàn)寫成類似的方式。
在「編譯時」你可以做很多繁雜的事情,就像高手過招前先養(yǎng)氣御劍一樣;這樣,在「運行時」,你才能打出行云流水的招式。
再舉一個例子。就寫 blog 而言,你可以用 wordpress,也可用 jekyll 這樣的 static site generator。前者把編譯和運行混在一起,在請求頁面的時候生成博文;而后者則將二者完全分離,你得使用 jekyll 的工具把 markdown 撰寫的博文編譯成 html,才能被正常訪問。這樣分離之后,天地開闊了很多,你可以在「編譯時」為所有文章生成全文搜索所用的索引,可以根據(jù)文章的類別 / tag 生成目錄,相關文章,菜單等等,在為運行時提供了閃電般的速度外,還能提供 wordpress 才能提供的動態(tài)性和靈活性。
注意,這里所說的分離完全是邏輯上的分離,就像上一篇文章中的 pipeline,每個 component 是邏輯上單獨存在,未必需要物理上完全分離。
把「編譯時」和「運行時」分離,是一項很重要的抽象能力。
版權申明:本站文章部分自網(wǎng)絡,如有侵權,請聯(lián)系:west999com@outlook.com
特別注意:本站所有轉載文章言論不代表本站觀點!
本站所提供的圖片等素材,版權歸原作者所有,如需使用,請與原作者聯(lián)系。
上一篇:Jetty 8長連接上的又一個坑
下一篇:Scala學習資源整理