【譯】從PHP(Laravel)遷移到Go
今年早些時候,我做出了一個糟糕的商業(yè)決定,我決定用Go重寫Laravel應(yīng)用Boxzilla,雖然沒有遺憾,最后效果驚人。

????僅僅幾個星期后我就部署了Go應(yīng)用程序。構(gòu)建它是我?guī)讉€月來最有趣的事情,我學(xué)到了很多東西,最終結(jié)果是對舊應(yīng)用程序的巨大改進(jìn)、更好的性能、更容易的部署和更高的測試覆蓋率。
????該應(yīng)用程序是一個相當(dāng)簡單的數(shù)據(jù)庫驅(qū)動的API和包含帳戶管理等應(yīng)用程序,用戶可以在登錄以后下載產(chǎn)品,查看發(fā)票或更新付款方式。
??? Stripe和Braintree用于接受訂閱付款。發(fā)票使用MoneyBird處理,一些事務(wù)性的電子郵件使用Mailgun發(fā)送。
????雖然Laravel在這方面做得足夠好,但有些事情總是讓我感到過于復(fù)雜。每隔幾個月發(fā)布一個新的“主要”版本,如果新的版本包含重大改進(jìn),我心里也會覺得這沒什么,但是很多時候,感覺都是一些非常簡單的改動,例如命名和目錄結(jié)構(gòu)進(jìn)行了更改。
Why Go?
? ? 去年,我已經(jīng)將幾種服務(wù)轉(zhuǎn)移到了Go上,所以我對這種語言并不是完全陌生。作為銷售基于WordPress的產(chǎn)品的開發(fā)人員,我的一部分工作是在一個古老的技術(shù)堆棧中工作,該堆棧主要關(guān)注最終用戶。
如果我不是自雇人士,我會簡單地申請一份新工作來彌補(bǔ)這種缺乏性感技術(shù)的現(xiàn)象。作為我自己的老板,我有責(zé)任使自己的日常工作變得有趣,而不僅僅是追逐更多的即時資金。如果收入允許(并且確實如此),為什么不找點樂子呢?
編寫Go代碼很高興,工具很棒,不僅開發(fā)速度快,而且最終結(jié)果通常也很快。剛讀完Go項目的目的(https://golang.org/doc/faq#What_is_the_purpose_of_the_project),就使我對語言產(chǎn)生了濃厚的興趣。
我認(rèn)為我們會在未來幾年內(nèi)看到大量人從動態(tài)類型的語言(如PHP,Python和JavaScript)切換到Go。
移植代碼庫
將代碼遷移到Go中主要包括正確地進(jìn)行數(shù)據(jù)庫交互以及將Blade模板移植到我們可以在Go中使用的東西。
ORM總是會阻礙我的一件事,因此我選擇了可模擬的數(shù)據(jù)訪問層和簡單的SQL查詢。使用Meddler擺脫了一些煩惱:將查詢結(jié)果映射到結(jié)構(gòu)體中。
為了支持分層模板和局部,我開源了grender(https://github.com/dannyvankooten/grender),這是基于Go的標(biāo)準(zhǔn)庫html / template包的一個小包裝。這使我可以相對輕松地將Blade模板文件移植到Go中,因為我可以使用相同的層次結(jié)構(gòu)和部分模板。
為了與Stripe集成,有官方的Stripe?-Go(https://github.com/stripe/stripe-go)軟件包。對于Braintree,有一個非正式的braintree-go(https://github.com/lionelbarrow/braintree-go)軟件包,該軟件包被忽略了一會兒,但最近又受到了新的關(guān)注。由于到目前為止,在Moneybird中還沒有Go軟件包來管理發(fā)票,因此我構(gòu)建并開源了moneybird-go(https://github.com/dannyvankooten/moneybird-go)。
對比分析
由于Go是一種編譯語言,具有比PHP更好的標(biāo)準(zhǔn)庫,因此像我將要比較的那樣比較這兩種語言并不是很公平。就是說,我認(rèn)為分享一些數(shù)字會很有趣。
性能
wrk用于為兩個返回登錄頁面HTML的應(yīng)用程序執(zhí)行一些簡單的HTTP基準(zhǔn)測試。
| ? | 并發(fā) | 平均?潛伏 | 要求/秒 | 傳輸/秒 |
|---|---|---|---|---|
| Laravel | 1 | 3.87ms | 261.48 | 1.27MB |
| Laravel | 100 | 108.86ms | 917.27 | 6.04MB |
| Go | 1 | 325.72μs | 7365.48 | 34.27MB |
| Go | 100 | 11.63ms | 19967.31 | 92.91MB |
| Go | 200 | 37.68ms | 22653.22 | 105.41MB |
不幸的是,一旦我將并發(fā)“用戶”的數(shù)量增加到100以上,Laravel應(yīng)用程序(或PHP-FPM套接字)就一直崩潰。
NetData提供了以下圖表,以查看服務(wù)器在所有這些負(fù)載下的承受能力。
100個并發(fā)連接的Go程序

100個并發(fā)連接的Laravel?程序

請注意,我在運行應(yīng)用程序的同一臺計算機(jī)上運行了基準(zhǔn)測試,因此這會嚴(yán)重影響兩個圖表。
代碼行數(shù)
讓我們比較兩個應(yīng)用程序中的代碼行,包括所有的依賴項。
$ find . -name '*.php' | xargs wc -l
156289 total
Laravel版本包含超過156.000行代碼。這還不包括要運行Laravel測試等相關(guān)的開發(fā)依賴包。
$ find . -name '*.go' | xargs wc -l
33624 total
另一方面,Go版本包含33.000行代碼。這是完全相同功能的代碼的五分之一。
讓我們在Laravel應(yīng)用程序中排除外部依賴關(guān)系,以便知道我實際寫了多少行php。
$ find . -name '*.php' -not -path "./vendor/*" | xargs wc -l
13921 total
多少行Go
$ find . -name '*.go' -not -path "./vendor/*" | xargs wc -l
6750 total
即使僅查看托管代碼行,結(jié)果也要稍微多一些。盡管如此,它還是只使用了原來完全相同的應(yīng)用程序的一半代碼。
測試范圍
測試是Go中的頭等公民,測試文件緊鄰實際源文件存在。
license.go
license_test.go
subscription.go
subscription_test.go
這使得應(yīng)用測試驅(qū)動的開發(fā)變得異常方便。
在我們的Laravel應(yīng)用程序中,我們主要進(jìn)行了集成測試,以檢查請求處理程序是否返回正確的響應(yīng)??傮w測試覆蓋率很低,主要是由于緊密耦合,而這又主要是我的錯。再次編寫相同的應(yīng)用程序確實也有幫助。
TLDR
做了一些您不應(yīng)該做的事情:用另一種語言重寫應(yīng)用程序,因為我感覺很喜歡。獲得了很多樂趣,并且得到了更小,更快的應(yīng)用程序。
