1.0 從并發編程本質了解Go高性能的本質
1.1 Goroutine
協程可以理解為輕量級線程;
Go更適合高并發場景原因之一:Go語言一次可以創建上萬協成;
“快速”:開多個協成 打印。
go func(): 在函數前加 go 代表 創建協程;
time.Sleep(): 協程阻塞,使主協程 在子協程結束前阻塞不退出;
亂序輸出 說明并行;
1.2 協程通信 CSP (Communicating Sequential Processes)
通過通信共享內存:Channel通道遵循 先入先出 保證順序
1.3Channel
Channel創建需要通過make()函數;
無緩沖通道也稱 同步通道;有緩沖:緩沖滿了之后要拿走才能存。
通過通信共享內存 例子:
M主函數作為消費者 實際中業務比較復雜,所以會 比 生產者AB慢;我們用 帶緩沖的通道 就不會因消費者M的消費速度 影響 生產者的執行效率;
1.4 并發安全Lock
addWithLock() 通過臨界區控制實現;在 每次 x+=1前后加解鎖;
addWithOutLock() 沒加鎖; Add 測試函數;對兩種實現做5個協程并發執行;
結果為:加鎖時輸出預期結果10000;體現不加鎖的并發安全問題
1.5 WaitGroup實現并發任務的同步
計算器=0表示所有并發任務結束;
1.5.1阻塞的優化
好的,以上就是對go并發編程相關概念的介紹,這里簡單做個小結
整個章節主要涉及3個方面,一個是協程,通過高效的調度模型實現高并發操作,一個是通道channel.通過通信實現共享內存;最后svnc相關關鍵字,實現并發安全操作和協程間的同步
2.0 依賴管理
這一章我們主要講解go的依賴管理, 主要涉及go依賴管理的演進路線和go module實踐
依賴指各種開發包
對于hello world以及類似的單體函數只需要依賴原生SDK,而實際工程會相對復雜,我們不可能基于標準庫0~1編碼搭建,而更多的關注業務邏輯的實現,而其他的涉及框架、日志、driver、以及collection等一系列依賴都會通過sdk的方式引入, 這樣對依賴包的管理就顯得尤為重要
2.1 Go依賴管理演進
2.1.1 GoPATH
GOPATH是Go語言支持的一個環境變量,value是Go項目的工作區。
目錄有以下結構:
src: 存放Go項目的源碼;pkg: 存放編譯的中間產物,加快編譯速度;
bin: 存放Go項目編譯生成的二進制文件,
大家想想用gopath依賴管理有 哪些弊端呢?
弊端
如圖,同一個pkg,有2個版本,A-> A0,B-> B0.
而src下只能有1個版本存在,那AB項目無法保證都能編譯通過。 也就是在gopath管理模式下,如果多個項目依賴同一個庫,則依賴該庫是同一份代碼,所以不同項目不能依賴同一個庫的不同版本,這很顯然不能滿足我們的項目依賴需求。為了解決這問題,govender出現了
2.1.2 GoVendor
Vendor是當前項目中的一個目錄,其中存放了當前項目依賴的副本,在Vendor機制下,如果當前項目存在Vendor目錄,會優先使用該目錄下的依賴,如果依賴不存在,會從GOPATH中尋找。 vendor無法很好解決依賴包的版本變動問題和一個項目依賴同一個包的不同版本的問題,下面我們看一個場景
如圖項目A依賴pkg b和c,而B和C依賴了D的不同版本,通過vendor的管理模式我們不能很好的控制對于D的依賴版本,一旦更新項目,有可能帶來依賴沖突。
歸根結底vendor不能清晰的標識依賴的版本概念原因是:他還是依賴源碼。
下面,go mod就應運而生了。
2.1.3 Go Module
Go Modules 是Go語言官方推出的依賴管理系統,解決了之前依賴管理系統存在的諸如無法依賴同一個庫的多個版本等問題
G0 module從1.11 開始實驗性引入,1.16 默認開啟;我們一般都讀為go mod,我們也先統一下名稱
2.2 依賴管理三要素
那其實完善的依賴管理一般都需要3要素,這里我們先整體介紹下
這里熟悉java的同學,可以類比下maven
2.3.1 依賴配置-go.mod
首先模塊路徑(依賴管理基本單元)用來標識一個模塊,從模塊路徑可以看出從哪里找到該模塊,如果是github前綴則表示可以從Github 倉庫找到該模塊,依賴包的源代碼由githu托管,如果項目的子包想被單獨引用,則需要通過單獨的go.mod文件進行管理。
下面是依賴的原生庫sdk版本(go 1.16)
最下面是單元依賴(最關鍵的部分),每個依賴單元用模塊路徑(跟上面的對應)+版本來唯一標示。
2.3.2 依賴配置-version
gopath和govendor都是源碼副本方式依賴,沒有版本規則概念
而gmod為了方便版本管理 定義了版本規則,分為語義化版本和偽版本
其中語義化版本包括三部分,
不同的MAJOR大版本可以表示是不兼容的
所以即使是同一個庫,MAJOR 版本不同也會被認為是不同的模塊
MINOR版本通常是新增函數或功能,需要保持在MAJOR下做到前后兼容 patch 版本一般是修復 bug ;
基于commit的偽版本包括3部分,
版本前綴是和語義化版本一樣的;
時間戳 yyyymmddhhmmss,也就是提交commit的時間,
最后是校驗碼(abcdefabcdef,包含 12 位的哈希前綴;每次提交commit后 Go 會默認生成一個偽版本號
2.3.3 依賴配置-非直接依賴indirect
下面我們再來看下依賴單元中的特殊標識符,首先是indirect后綴
表示go.mod對應的當前模塊,沒有直接導入該依賴模塊的包,也就是非直接依賴,標示間接依賴,例如
2.3.4 依賴配置-incompatible
下一個常見是的是incompatible
主版本2+模塊(v2以上) 會在模塊路徑增加/vN(v1,v2這種后綴),這讓gomod按照不同的模塊來處理同一個項目不同主版本的依賴(允許不同MAJOR版本間相互兼容)
由于gomod是1.11實驗性引入,所以這項提出之前已經有一些倉庫打上了v2或者更高版本的tag了,為了兼容這部分倉庫,對于沒有go.mod文件并且主版本在2或者以上的依賴,會在版本號后加上+incompatible 后綴
前面講語義化版本提到,對于同一個庫的不同的major版本,需要建立不同的pkg目錄,用不同的gomod文件管理
如下面倉庫為例,V1版本gomod在主目錄下,而對于V2版本,則單獨建立了V2目錄,用另一個gomod文件管理依賴路徑,來表明不同major的不兼容性。
那對于有些V2+tag版本的依賴包并未遵循這定義規則,就會打上incompatible標志,增加一個compatile的case
依賴圖
答案竟然是B!
Go底層會根據自己的算法 選擇最 低的兼容版本
2.3.5 依賴分發-回源
gomodule的依賴分發,也就是從哪里下載,如何下載的問題
github是比較常見給的代碼托管系統平臺,而Go Modules 系統中定義的依賴,最終可以對應到多版本代碼管理系統中某一項目的特定提交或版本,這樣的話,對于go.mod中定義的依賴,則直接可以從對應倉庫中下載指定軟件依賴,從而完成依賴分發。
但直接使用版本管理倉庫下載依賴,存在多個問題
首先無法保證構建確定性:軟件作者可以直接碼平臺增加/修改/刪除 軟件版本,導致下次構建使用另外版本的依賴,或者找不到依賴版本。
無法保證依賴可用性:依賴軟件作者可以直接代碼平臺刪除軟件,導致依賴不可用;大幅增加第三方代碼托管平臺壓力。
2.3.5 依賴分發-Proxy
而go proxy就是解決這些問題的方案,Go Proxy 是一個服務站點
它會緩存源站中的軟件內容,緩存的軟件版本不會改變,并且在源站軟件刪除之后依然可用,從而實現了供“immutability”和“available”的依賴分發;
使用 Go Proxy 之后,構建時會直接從 Go Proxy 站點拉取依賴。
類比項目中,如果下游無法滿足我們上游的需求、接口,我們可以建一層適配器或Proxy解決
2.3.6 依賴分發-變量-GOPROXY
下面講一下go proxy的使用,Go Modules通過GOPROXY環境變量控制如何使用 Go Proxy;GOPROXY是一個Proxy 站點URL列表,可以使用 “direct”表示源站 用逗號分隔。
對于示例配置,整體的依賴尋址路徑,會優先從proxy1下載依賴,如果proxy1不存在,后下鉆proxy2尋找,如果proxy2,中不存在則會回源到源站直接下載依賴,緩存到proxy站點中。
2.3.7 工具-go get
對go module的管理工具介紹下使用,首先是go get
2.3.8 工具-go mod
go mod,盡量提交之前執行下go tidy,減少構建時無效依賴包的拉取
非常感謝您閱讀到這里,如果這篇文章對您有幫助,希望能留下您的點贊👍 關注💖 收藏 💕評論💬感謝支持!!!