Go語言中程序是怎么編譯的實現(xiàn)
在當(dāng)今快速發(fā)展的軟件開發(fā)領(lǐng)域,Go 語言(又稱 Golang)已經(jīng)成為了開發(fā)高性能應(yīng)用程序的熱門選擇。由 Google 開發(fā)并在 2009 年公開發(fā)布,Go 語言因其簡潔的語法、出色的并發(fā)支持以及優(yōu)秀的性能而受到廣泛歡迎。盡管 Go 的語法相對簡單,但它的編譯過程和模塊管理系統(tǒng)可能會讓初學(xué)者感到有些困惑,特別是那些從動態(tài)語言背景轉(zhuǎn)來的開發(fā)者。
本文旨在深入探討 Go 語言的編譯機制和最新的模塊管理系統(tǒng)——Go Modules。通過詳細(xì)的示例和步驟,我們將演示從簡單的 “Hello World” 程序到使用第三方庫的更復(fù)雜項目的開發(fā)過程。我們將開始于基本的編譯命令,探索如何手動處理依賴,然后過渡到使用 Go Modules 管理依賴,這在現(xiàn)代 Go 項目開發(fā)中已成為標(biāo)準(zhǔn)實踐。
1、編譯和運行 Go 程序的基礎(chǔ)
在上一篇我們實現(xiàn) HelloWorld 程序的過程中,我們在運行 “helloworld” 程序之前,是輸入了一個 go build
命令的:
$go build main.go
對此,如果有使用 C/C++ 和 gcc 或 clang 的開發(fā)經(jīng)驗的同學(xué)家就會發(fā)現(xiàn) Go 的編譯步驟與它們非常類似。都是在編譯成功后,生成一個二進制可執(zhí)行文件。
在 Linux 系統(tǒng)、macOS 系統(tǒng)或 Windows 系統(tǒng)的 PowerShell 中,可以通過執(zhí)行以下 ls 命令來查看這個新生成的可執(zhí)行文件:
$ls main* main.go
可以看到,在上面顯示的文件里面有我們剛剛創(chuàng)建的、以 .go
為后綴的源代碼文件,還有剛生成的可執(zhí)行文件(Windows 系統(tǒng)下為 main.exe
,其余系統(tǒng)下為 main
)。
如果你主要使用的是像 Ruby、Python 或 JavaScript 這樣的動態(tài)語言,那么你可能不太習(xí)慣需要在運行程序之前先進行編譯的步驟。
Go 是編譯型語言,這意味著我們必須先編譯 Go 程序,才能將生成的可執(zhí)行文件交給其他人,在沒有安裝 Go 的環(huán)境中也能運行這些程序。相對地,如果你提供給別人的是 .rb
、.py
或 .js
文件,他們就需要在自己的環(huán)境中安裝相應(yīng)的 Ruby、Python 或 JavaScript 解釋器來執(zhí)行這些動態(tài)語言的源代碼。
當(dāng)然,Go 也借鑒了動態(tài)語言的一些對開發(fā)者體驗較好的特性,比如基于源碼文件的直接執(zhí)行,Go 提供了 run 命令可以直接運行 Go 源碼文件,比如我們也可以使用下面命令直接基于 main.go
運行:
$go run main.go hello, world
不過像 go run
這類命令更多用于開發(fā)調(diào)試階段,真正的交付成果還是需要使用 go build 命令
構(gòu)建的。
然而,在我們的生產(chǎn)環(huán)境中,編譯 Go 程序通常不像之前提到的基于單個 Go 源文件構(gòu)建像"hello, world"這樣的簡單示例那么直接。隨著項目接近真實的生產(chǎn)條件,它的規(guī)模會增大、涉及的協(xié)作人員會增多,同時項目的依賴及其版本也會變得更為復(fù)雜。
2、Go Modules 機制
2.1、復(fù)雜項目下 Go 程序的編譯
現(xiàn)在,我們將啟動一個名為"hellomod"的新項目,在這個項目中,我們會使用兩個第三方庫:zap
和 fasthttp
。這將使 go build
的構(gòu)建過程更具挑戰(zhàn)性。
如同我們之前的"helloworld"示例一樣,我們可以通過以下命令來創(chuàng)建“hellomod”項目:
$mkdir hellomod $cd hellomod
接著,我們在"hellomod"下創(chuàng)建并編輯我們的示例源碼 main.go
文件:
package main import ( "github.com/valyala/fasthttp" "go.uber.org/zap" ) var logger *zap.Logger func init() { logger, _ = zap.NewProduction() } func fastHTTPHandler(ctx *fasthttp.RequestCtx) { logger.Info("hello, go module", zap.ByteString("uri", ctx.RequestURI())) } func main() { fasthttp.ListenAndServe(":8081", fastHTTPHandler) }
這個示例中,我們創(chuàng)建了一個在 8081 端口上監(jiān)聽的 HTTP 服務(wù)。當(dāng)向該服務(wù)發(fā)起請求時,它會在終端的標(biāo)準(zhǔn)輸出上打印一段訪問日志???/p>
以看出,相比于“hello, world”示例,這個項目明顯更加復(fù)雜。但目前,我們不需要深入了解每行代碼的具體作用。只要應(yīng)該我們在這個稍微復(fù)雜的示例中使用了兩個第三方依賴庫:zap
和 fasthttp
。
接下來,讓我們嘗試使用編譯"hello, world"時的方法來編譯“hellomod”項目中的 main.go
源文件,看看 Go 編譯器會給出什么樣的輸出結(jié)果:
$go build main.go main.go:4:3: no required module provides package github.com/valyala/fasthttp: go.mod file not found in current directory or any parent directory; see 'go help modules' main.go:5:3: no required module provides package go.uber.org/zap: go.mod file not found in current directory or any parent directory; see 'go help modules'
從編譯器的輸出來看,go build 似乎在找一個名為 go.mod 的文件,來解決程序?qū)Φ谌桨囊蕾嚊Q策問題。
2.2、go mod
Go 1.11 版本推出 modules 機制,簡稱 mod,更加易于管理項目中所需要的模塊。模塊是存儲在文件樹中的 Go 包的集合,其根目錄中包含 go.mod 文件。 go.mod 文件定義了模塊的模塊路徑,它也是用于根目錄的導(dǎo)入路徑,以及它的依賴性要求。每個依賴性要求都被寫為模塊路徑和特定語義版本。
從 Go 1.11
開始,Go
允許在 $GOPATH/src
外的任何目錄下使用 go.mod
創(chuàng)建項目。在 $GOPATH/src
中,為了兼容性,Go
命令仍然在舊的 GOPATH
模式下運行。從 Go 1.13
開始,go.mod
模式將成為默認(rèn)模式。
2.3、go mod 示例
現(xiàn)在,我們將通過以下命令為“hellomod”示例程序創(chuàng)建一個 go.mod
文件:
go mod init github.com/lizhengi/hellomod go: creating new go.mod: module github.com/lizhengi/hellomod go: to add module requirements and sums: go mod tidy
Ps:注意模塊命名方式,如果你的項目要發(fā)布到 github,則命名為:
github.com/<USERNAME>/<MODULE_NAME>
此時可以看到,go mod init
命令的執(zhí)行結(jié)果是在當(dāng)前目錄下生成了一個 go.mod 文件:
$cat go.mod module github.com/lizhengi/hellomod go 1.17
實際上,一個 module 是多個包的集合,這些包與 module 一起進行版本控制、發(fā)布和分發(fā)。go.mod
文件所在的目錄被視為聲明的 module 的根目錄。此時的 go.mod
文件內(nèi)容相對簡單,首行是用來聲明 module 路徑(module path)的。Module 本質(zhì)上引入了命名空間的概念,這意味著 module 下每個包的導(dǎo)入路徑都由 module path 和包所在的子目錄名共同構(gòu)成。
例如,在 hellomod
下有一個子目錄 pkg/pkg1
,那么 pkg1
中的包的導(dǎo)入路徑將是 github.com/lizhengi/hellomod/pkg/pkg1
。此外,go.mod
文件的最后一行是 Go 版本指示符,表示該 module 是基于特定 Go 版版本的 module 語義編寫的。
此時,我們執(zhí)行一下構(gòu)建,Go 編譯器輸出結(jié)果是這樣的
$go build main.go main.go:4:2: no required module provides package github.com/valyala/fasthttp; to add it: go get github.com/valyala/fasthttp main.go:5:2: no required module provides package go.uber.org/zap; to add it: go get go.uber.org/zap
你會看到,Go 編譯器提示源碼依賴 fasthttp 和 zap 兩個第三方包,但是 go.mod 中沒有這兩個包的版本信息,我們需要按提示手工添加信息到 go.mod 中。
這個時候,除了按提示手動添加外,我們也可以使用 go mod tidy 命令,讓 Go 工具自動添加:
$go mod tidy go: finding module for package go.uber.org/zap go: finding module for package github.com/valyala/fasthttp go: downloading github.com/valyala/fasthttp v1.54.0 go: downloading go.uber.org/zap v1.27.0 go: found github.com/valyala/fasthttp in github.com/valyala/fasthttp v1.54.0 go: found go.uber.org/zap in go.uber.org/zap v1.27.0 go: downloading go.uber.org/multierr v1.10.0 go: downloading github.com/stretchr/testify v1.8.1 go: downloading go.uber.org/goleak v1.3.0 go: downloading github.com/andybalholm/brotli v1.1.0 go: downloading github.com/klauspost/compress v1.17.7 go: downloading github.com/valyala/bytebufferpool v1.0.0 go: downloading gopkg.in/yaml.v3 v3.0.1 go: downloading github.com/davecgh/go-spew v1.1.1 go: downloading github.com/pmezard/go-difflib v1.0.0
Ps:超時的話換個代理即可
go env -w GO111MODULE=on go env -w GOPROXY=https://goproxy.cn,direct
Go 工具不僅下載并添加了 hellomod 直接依賴的 zap 和 fasthttp 包的信息,還下載了這兩個包的相關(guān)依賴包。go mod tidy 執(zhí)行后,我們 go.mod 的最新內(nèi)容變成了這個樣子:
module github.com/bigwhite/hellomod go 1.16 require ( github.com/valyala/fasthttp v1.28.0 go.uber.org/zap v1.18.1 )
此時,go.mod
文件已經(jīng)包含了 hellomod
直接依賴的包的信息。此外,在 hellomod
目錄下還出現(xiàn)了一個名為 go.sum
的文件,這個文件記錄了 hellomod
的直接依賴和間接依賴包的版本 hash 值,這用于校驗本地包的真實性。在構(gòu)建過程中,如果本地依賴包的 hash 值與 go.sum
文件中的記錄不匹配,構(gòu)建會被拒絕。
有了 go.mod 以及 hellomod 依賴的包版本信息后,我們再來執(zhí)行構(gòu)建:
$go build main.go go: downloading go.uber.org/zap v1.27.0 go: downloading github.com/valyala/fasthttp v1.54.0 go: downloading go.uber.org/multierr v1.10.0 go: downloading github.com/andybalholm/brotli v1.1.0 go: downloading github.com/klauspost/compress v1.17.7 go: downloading github.com/valyala/bytebufferpool v1.0.0 $ls go.mod go.sum main* main.go
這次我們成功構(gòu)建出了可執(zhí)行文件 main,運行這個文件,新開一個終端窗口,在新窗口中使用 curl 命令訪問該 http 服務(wù):curl localhost:8081/foo/bar
,我們就會看到服務(wù)端輸出如下日志:
$./main {"level":"info","ts":1716950836.531074,"caller":"hellomod/main.go:15","msg":"hello, go module","uri":"/foo/bar"}
到此這篇關(guān)于Go語言中程序是怎么編譯的實現(xiàn)的文章就介紹到這了,更多相關(guān)Go語言編譯內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語言實現(xiàn)一個Http?Server框架(一)?http庫的使用
本文主要介紹用Go語言實現(xiàn)一個Http?Server框架中對http庫的基本使用說明,文中有詳細(xì)的代碼示例,感興趣的同學(xué)可以借鑒一下2023-04-04golang?recover函數(shù)使用中的一些坑解析
這篇文章主要為大家介紹了golang?recover函數(shù)使用中的一些坑解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-02-02基于context.Context的Golang?loader緩存請求放大問題解決
這篇文章主要為大家介紹了基于context.Context的Golang?loader緩存請求放大解決方案,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-05-05golang簡易實現(xiàn)?k8s?的yaml上傳并應(yīng)用示例方案
這篇文章主要為大家介紹了golang簡易實現(xiàn)?k8s?的yaml上傳并應(yīng)用示例方案,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-07-07Go雪花算法的作用領(lǐng)域及實現(xiàn)方法示例
這篇文章主要為大家介紹了Go雪花算法的作用領(lǐng)域及實現(xiàn)方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10golang gin 監(jiān)聽rabbitmq隊列無限消費的案例代碼
這篇文章主要介紹了golang gin 監(jiān)聽rabbitmq隊列無限消費,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-12-12