A. Golang database/sql源碼分析
Gorm是Go語言開發用的比較多的一個ORM。它的功能比較全:
但是這篇文章中並不會直接看Gorm的源碼,我們會先從database/sql分析。原因是Gorm也是基於這個包來封裝的一些功能。所以只有先了解了database/sql包才能更加好的理解Gorm源碼。
database/sql 其實也是一個對於mysql驅動的上層封裝。」github.com/go-sql-driver/mysql」就是一個對於mysql的驅動,database/sql 就是在這個基礎上做的基本封裝包含連接池的使用
下面這個是最基本的增刪改查操作
操作分下面幾個步驟:
因為Gorm的連接池就是使用database/sql包中的連接池,所以這里我們需要學習一下包里的連接池的源碼實現。其實所有連接池最重要的就是連接池對象、獲取函數、釋放函數下面來看一下database/sql中的連接池。
DB對象
獲取方法
釋放連接方法
連接池的實現有很多方法,在database/sql包中使用的是chan阻塞 使用map記錄等待列表,等到有連接釋放的時候再把連接傳入等待列表中的chan 不在阻塞返回連接。
之前我們看到的Redigo是使用一個chan 來阻塞,然後釋放的時候放入空閑列表,在往這一個chan中傳入struct{}{},讓程序繼續 獲取的時候再從空閑列表中獲取。並且使用的是鏈表的結構來存儲空閑列表。
database/sql 是對於mysql驅動的封裝,然而Gorm則是對於database/sql的再次封裝。讓我們可以更加簡單的實現對於mysql資料庫的操作。
B. Go 是怎麼使用 Go 來編譯自身的
第一步:all.bash
% cd $GOROOT/src
% ./all.bash
第一步有些突兀,因為 all.bash 僅僅調用了其它兩個 shell 腳本;make.bash 和 run.bash。如果你在使用 Windows 或 Plan 9,過程是一樣的,只是腳本擴展名變成了.bat 或.rc。對於本文中的其它腳本,請根據你的系統適當改動。
第二步:make.bash
. ./make.bash --no-banner
main.bash 來源於 all.bash,因此調用退出將正確終止便宜進程。main.bash 有三個主要工作,第一個是驗證編譯 Go 的環境是否完整。完整性檢查在過去幾年中建立,它通常嘗試避免使用已知的破損工具或必然失敗的環境進行編譯。
第三步. cmd/dist
gcc -O2 -Wall -Werror -ggdb -o cmd/dist/dist -Icmd/dist cmd/dist/*.c
一旦可用性檢查完畢,make.bash 將編譯產生 cmd/dist,cmd/dist取代了之前存在於Go 1 之前的Makefile 編譯系統。cmd/dist用來管理少量的pkg/runtime的代碼生成。cmd/dist 是C語言編寫的程序,能夠充分利用系統C編譯器和頭文件來處理大部分主機系統平台的檢測。cmd/dist通常用來檢測主機的操作系統和體系結構,即環境變數$GOHOSTOS和$GOHOSTARCH .如果是交叉編譯的話,變數 $GOOS和$GOARCH可能會由於你的設置而不同。事實上,Go 通常用作跨平台編譯器,只不過多數情況下,主機和目標系統一致而已。接下來,make.bash 調用cmd/dist 的引導參數的支持庫、 lib9、 libbio 和 libmach,使用編譯器套件,然後用自己的編譯器進行編譯。這些工具也是用 C 語言寫的中,但是由系統 C 編譯器編譯產生。
echo "# Building compilers and Go bootstrap tool for host, $GOHOSTOS/$GOHOSTARCH."
buildall="-a"
if [ "$1" = "--no-clean" ]; then
buildall=""
fi
./cmd/dist/dist bootstrap $buildall -v # builds go_bootstrap
使用的編譯器套件 cmd/dist 編譯產生一個版本的gotool,go_bootstrap。但go_bootstrap並不是完整得gotool,比方說 pkg/net 就是孤立的,避免了依賴於 cgo。要編譯的文件的列表以及它們的依賴項,是由cmd/dist編譯的 ,所以十分謹慎地避免引入新的生成依賴項 到 cmd/go。
第四步:go_bootstrap
現在, go_bootstrap 編譯完成了,make.bash 的最後一部就是使用 go_bootstrap 完成 Go 標准庫的編譯,包括整套 gotool 的替換版。
echo "# Building packages and commands for $GOOS/$GOARCH."
"$GOTOOLDIR"/go_bootstrap install -gcflags "$GO_GCFLAGS" \
-ldflags "$GO_LDFLAGS" -v std
第五步:run.bash
現在,make.bash 完成了,運行回到了 all.bash,它將引用 run.bash。run.bash 的工作是編譯和測試標准庫,運行時以及語言測試套件。
bash run.bash --no-rebuild
使用 --no-rebuild 標識是因為 make.bash 和 run.bash 可能都調用了 go install -a std,這樣可以避免重復,--no-rebuild 跳過了第二個 go install。
# allow all.bash to avoid double-build of everything
rebuild=true
if [ "$1" = "--no-rebuild" ]; then
shift
else
echo '# Building packages and commands.'
time go install -a -v std
echo
fi
第六步:go test -a std
echo '# Testing packages.'
time go test std -short -timeout=$(expr 120 \* $timeout_scale)s
echo
下一步 run.bash z則是對標准庫中的所有包進行單元測試,這是使用 testing 包編寫的。由於 $GOPATH 和 $GOROOT 中的代碼存在於同一個命名空間中,我們不能使用 go test,這可能會測試 $GOPATH 中的所有包,所以將創建別名std來標識標准庫中的包。由於有些測試需要很長時間,或耗用大量內存,測試將會通過 -short 標識將其過濾。
第七步 runtime 和 cgo 測試
run.bash的下一節將運行大量對cgo支持的平台測試,運行一些季春測試,編譯 Go 附帶的一些雜項程序。隨著時間的推移,這份雜項程序列表已經變長了,當它們發現自己並不包含在編譯過程中時,沉默將不可避免的被打破。
第八步: go run test
(xcd ../test
unset GOMAXPROCS
time go run run.go
) || exit $?
run.bash的倒數第二步調用了$GOROOT目錄下test文件夾中的編譯器和運行時測試。這其中有描述編譯器和運行時本身的低層級測試。而子目錄 test/bugs 及 test/fixedbugs 中的測試對已知問題和已解決問題進行特別的測試。所有測試的測試驅動器是 $GOROOT/test/run.go,該程序很小,它調用test文件夾中的每個.go 文件。有些 .go 文件在首行上描述了預期的運行結果,例如,程序失敗或是放出特定的輸出隊列。
第九步go tool api
echo '# Checking API compatibility.'
go tool api -c $GOROOT/api/go1.txt,$GOROOT/api/go1.1.txt \
-next $GOROOT/api/next.txt -except $GOROOT/api/except.txt
run.bash的最後一部將調用API工具,API工具的作用是執行 Go 1 約定;導出的符號,常數,函數,變數,類型和方法組成2012年確認的 Go 1 API。Go 1 寫在 api/go1.txt 文件,而 Go 1.1 則寫在 api/go1.1.txt文件中。另一個額外的文件,api/next.txt 描述了G 1.1自後添加到標准庫和運行時中的符號。當 Go 1.2 發布時,這個文件將會成為 Go 1.2 的約定,另一個新的 next.txt 文件也將被創建。這里還有一個小文件,except.txt,它包括 Go 1 約定中被批準的擴展。對文件的增添總是小心翼翼的。
C. liteide 怎麼查看go語言自帶源碼包
LiteIDE是一款專門為Go語言開發的跨平台輕量級集成開發環境(IDE),由QT編寫。LiteIDE主要特點:支持主流操作系統WindowsLinuxMacOSXGo編譯環境管理和切換管理和切換多個Go編譯環境支持Go語言交叉編譯與Go標准一致的項目管理方式基於GOPAT
D. 開始讀 Go 源碼了
學完 Go 的基礎知識已經有一段時間了,那麼接下來應該學什麼呢?有幾個方向可以考慮,比如說 Web 開發,網路編程等。
在下一階段的學習之前,寫了一個開源項目 Go 開發的一款分布式唯一 ID 生成系統,如果你對這個項目感興趣的話,可以在 GitHub 上拿到源碼。
在寫項目的過程中,發現一個問題。實現功能是沒問題的,但不知道自己寫的代碼是不是符合 Go 的風格,是不是夠優雅。所以我覺得相比於繼續學習應用開發,不如向底層前進,打好基礎,打好寫 Go 代碼的基礎。
所以,我決定開始讀 Go 標准庫源碼,Go 一共有 150+ 標准庫,想要全部讀完的話不是不可能,但絕對是一項大工程,希望自己能堅持下去。
為什麼從 Go 標准庫的源碼開始讀呢?因為最近也看了一些 Go 底層原理的書,說實話,像 goroutine 調度,gc 垃圾回收這些內容,根本就看不懂。這要是一上來就讀這部分代碼,恐怕直接就放棄 Go 語言學習了。
而標准庫就不一樣了,有一部分代碼根本不涉及底層原理,實現也相對簡單,同時又能對 Go 的理念加深理解,作為入門再好不過了。然後再由簡入深,循序漸進,就像打怪升級一樣,一步一步征服 Go。
說了這么多,那到底應該怎麼讀呢?我想到了一些方法:
可以通過上面的一種或幾種方法相結合,然後再不斷閱讀不斷總結,最終找到一個完全適合自己的方法。
下面是我總結的一些標准庫及功能介紹:
這里僅僅列舉了一部分標准庫,更全面的標准庫列表大家可以直接看官網。
那麼問題來了,這么多庫從何下手呢?
我這里做一個簡單的分類,由於水平有限,只能做一些簡單的梳理,然後大家可以結合自己的實際情況來做選擇。
有些庫涉及到非常專業的知識,投入產出比可能會比較低。比如 archive 、 compress 以及 crypto ,涉及到壓縮演算法以及加密演算法的知識。
有些庫屬於工具類,比如 bufio 、 bytes 、 strings 、 path 、 strconv 等,這些庫不涉及領域知識,閱讀起來比較容易。
有些庫屬於與操作系統打交道的,比如 os , net 、 sync 等,學習這些庫需要對操作系統有明確的認識。
net 下的很多子包與網路協議相關,比如 net/http ,涉及 http 報文的解析,需要對網路協議比較了解。
如果想要深入了解語言的底層原理,則需要閱讀 runtime 庫。
要想快速入門,並且了解語言的設計理念,建議閱讀 io 以及 fmt 庫,閱讀後會對介面的設計理解更深。
我已經看了一些源碼,雖然過程痛苦,但確實非常有用。前期可能理解起來比較困難,用的時間長一些,但形成固定套路之後,會越來越熟悉,用的時間也會更少,理解也會更深刻。
開源項目: