您的位置:  首頁 > 技術 > go語言 > 正文

go-zero微服務實戰系列(十一、大結局)

2022-07-11 15:01 https://my.oschina.net/kevwan/blog/5551919 kevwan 次閱讀 條評論

本篇是整個系列的最后一篇了,本來打算在系列的最后一兩篇寫一下關于k8s部署相關的內容,在構思的過程中覺得自己對k8s知識的掌握還很不足,在自己沒有理解掌握的前提下我覺得也很難寫出自己滿意的文章,大家看了可能也會覺得內容沒有干貨。我最近也在學習k8s的一些最佳實踐以及閱讀k8s的源碼,等待時機成熟的時候可能會考慮單獨寫一個k8s實戰系列文章。

內容回顧

下面列出了整個系列的每篇文章,這個系列文章的主要特點是貼近真實的開發場景,并針對高并發請求以及常見問題進行優化,文章的內容也是循序漸進的,先是介紹了項目的背景,接著進行服務的拆分,拆分完服務進行API的定義和表結構的設計,這和我們實際在公司中的開發流程是類似的,緊接著就是做一些數據庫的CRUD基本操作,后面用三篇文章來講解了緩存,因為緩存是高并發的基礎,沒有緩存高并發系統就無從談起,緩存主要是應對高并發的讀,接下來又用兩篇文章來對高并發的寫進行優化,最后通過分布式事務保證為服務間的數據一致性。如果大家能夠對每一篇文章都能理解透徹,我覺得對于工作中的絕大多數場景都能輕松應對。

對于文章配套的示例代碼并沒有寫的很完善,有幾點原因,一是商城的功能點非常多,很難把所有的邏輯都覆蓋到;二是多數都是重復的業務邏輯,只要大家掌握了核心的示例代碼,其他的業務邏輯可以自己把代碼down下來進行補充完善,這樣我覺得才會進步。如果有不理解的地方大家可以在社區群中問我,每個社區群都可以找到我。

go-zero微服務實戰系列(一、開篇)

go-zero微服務實戰系列(二、服務拆分)

go-zero微服務實戰系列(三、API定義和表結構設計)

go-zero微服務實戰系列(四、CRUD熱身)

go-zero微服務實戰系列(五、緩存代碼怎么寫)

go-zero微服務實戰系列(六、緩存一致性保證)

go-zero微服務實戰系列(七、請求量這么高該如何優化)

go-zero微服務實戰系列(八、如何處理每秒上萬次的下單請求)

go-zero微服務實戰系列(九、極致優化秒殺性能)

go-zero微服務實戰系列(十、分布式事務如何實現)

單元測試

軟件測試由單元測試開始(unit test)。更復雜的測試都是在單元測試之上進行的。如下所示測試的層級模型:

單元測試(unit test)是最小、最簡單的軟件測試形式、這些測試用來評估某一個獨立的軟件單元,比如一個類,或者一個函數的正確性。這些測試不考慮包含該軟件單元的整體系統的正確定。單元測試同時也是一種規范,用來保證某個函數或者模塊完全符合系統對其的行為要求。單元測試經常被用來引入測試驅動開發的概念。

go test工具

go語言的測試依賴go test工具,它是一個按照一定約定和組織的測試代碼的驅動程序。在包目錄內,所有以_test.go為后綴的源代碼文件都是 go test 測試的一部分,不會被go build編譯到最終的可執行文件。

*_test.go文件中有三種類型的函數,單元測試函數、基準測試函數和示例函數:

類型格式作用
測試單數函數名前綴為Test測試程序的一些邏輯行為是否正確
基準函數函數名前綴為Benchmark測試函數的性能
示例函數函數名前綴為Example提供示例

go test會遍歷所有*_test.go文件中符合上述命名規則的函數,然后生成一個臨時的main包用于調用相應的測試函數。

單測格式

每個測試函數必須導入testing包,測試函數的基本格式如下:

func?TestName(t?*testing.T)?{
	//?......
}

測試函數的名字必須以Test開頭,可選的后綴名必須以大寫字母開頭,示例如下:

func?TestDo(t?*testing.T)?{?//......?}
func?TestWrite(t?*testing.T)?{?//?......?}

testing.T 用于報告測試失敗和附加的日志信息,擁有的主要方法如下:

Name()?string
Fail()
Failed()?bool
FailNow()
logDepth(s?string,?depth?int)
Log(args?...any)
Logf(format?string,?args?...any)
Error(args?...any)
Errorf(format?string,?args?...any)
Fatal(args?...any)
Fatalf(format?string,?args?...any)
Skip(args?...any)
Skipf(format?string,?args?...any)
SkipNow()
Skipped()?bool
Helper()
Cleanup(f?func())
Setenv(key?string,?value?string)

簡單示例

在這個路徑下lebron/apps/order/rpc/internal/logic/createorderlogic.go:44 有一個生成訂單id的函數,函數如下:

func?genOrderID(t?time.Time)?string?{
	s?:=?t.Format("20060102150405")
	m?:=?t.UnixNano()/1e6?-?t.UnixNano()/1e9*1e3
	ms?:=?sup(m,?3)
	p?:=?os.Getpid()?%?1000
	ps?:=?sup(int64(p),?3)
	i?:=?atomic.AddInt64(&num,?1)
	r?:=?i?%?10000
	rs?:=?sup(r,?4)
	n?:=?fmt.Sprintf("%s%s%s%s",?s,?ms,?ps,?rs)
	return?n
}

我們創建createorderlogic_test.go文件并為該方法編寫對應的單元測試函數,生成的訂單id長度為24,單元測試函數如下:

func?TestGenOrderID(t?*testing.T)?{
	oid?:=?genOrderID(time.Now())
	if?len(oid)?!=?24?{
		t.Errorf("oid?len?expected?24,?got:?%d",?len(oid))
	}
}

在當前路徑下執行 go test 命令,可以看到輸出結果如下:

PASS
ok??	github.com/zhoushuguang/lebron/apps/order/rpc/internal/logic	1.395s

還可以加上 -v 輸出更完整的結果,go test -v 輸出結果如下:

===?RUN???TestGenOrderID
---?PASS:?TestGenOrderID?(0.00s)
PASS
ok??	github.com/zhoushuguang/lebron/apps/order/rpc/internal/logic	1.305s

go test -run

在執行 go test 命令的時候可以添加 -run 參數,它對應一個正則表達式,又有函數名匹配上的測試函數才會被 go test 命令執行,例如我們可以使用 go test -run=TestGenOrderID 來值運行 TestGenOrderID 這個單測。

表格驅動測試

表格驅動測試不是工具,它只是編寫更清晰測試的一種方式和視角。編寫好的測試并不是一件容易的事情,但在很多情況下,表格驅動測試可以涵蓋很多方面,表格里的每一個條目都是一個完整的測試用例,它包含輸入和預期的結果,有時還包含測試名稱等附加信息,以使測試輸出易于閱讀。使用表格測試能夠很方便的維護多個測試用例,避免在編寫單元測試時頻繁的復制粘貼。

lebron/apps/product/rpc/internal/logic/checkandupdatestocklogic.go:53 我們可以編寫如下表格驅動測試:

func?TestStockKey(t?*testing.T)?{
	tests?:=?[]struct?{
		name???string
		input??int64
		output?string
	}{
		{"test?one",?1,?"stock:1"},
		{"test?two",?2,?"stock:2"},
		{"test?three",?3,?"stock:3"},
	}

	for?_,?ts?:=?range?tests?{
		t.Run(ts.name,?func(t?*testing.T)?{
			ret?:=?stockKey(ts.input)
			if?ret?!=?ts.output?{
				t.Errorf("input:?%d?expectd:?%s?got:?%s",?ts.input,?ts.output,?ret)
			}
		})
	}
}

執行命令 go test -run=TestStockKey -v 輸出如下:

===?RUN???TestStockKey
===?RUN???TestStockKey/test_one
===?RUN???TestStockKey/test_two
===?RUN???TestStockKey/test_three
---?PASS:?TestStockKey?(0.00s)
????---?PASS:?TestStockKey/test_one?(0.00s)
????---?PASS:?TestStockKey/test_two?(0.00s)
????---?PASS:?TestStockKey/test_three?(0.00s)
PASS
ok??	github.com/zhoushuguang/lebron/apps/product/rpc/internal/logic	1.353s

并行測試

表格驅動測試中通常會定義比較多的測試用例,而go語言又天生支持并發,所以很容易發揮自身優勢將表格驅動測試并行化,可以通過t.Parallel() 來實現:

func?TestStockKeyParallel(t?*testing.T)?{
??t.Parallel()
	tests?:=?[]struct?{
		name???string
		input??int64
		output?string
	}{
		{"test?one",?1,?"stock:1"},
		{"test?two",?2,?"stock:2"},
		{"test?three",?3,?"stock:3"},
	}

	for?_,?ts?:=?range?tests?{
		ts?:=?ts
		t.Run(ts.name,?func(t?*testing.T)?{
			t.Parallel()
			ret?:=?stockKey(ts.input)
			if?ret?!=?ts.output?{
				t.Errorf("input:?%d?expectd:?%s?got:?%s",?ts.input,?ts.output,?ret)
			}
		})
	}
}

測試覆蓋率

測試覆蓋率是指代碼被測試套件覆蓋的百分比。通常我們使用的都是語句的覆蓋率,也就是在測試中至少被運行一次的代碼占總的代碼的比例。go提供內置的功能來檢查代碼覆蓋率,即使用 go test -cover 來查看測試覆蓋率:

PASS
coverage:?0.6%?of?statements
ok??	github.com/zhoushuguang/lebron/apps/product/rpc/internal/logic	1.381s

可以看到我們的覆蓋率只有 0.6% ,哈哈,這是非常不合格滴,大大的不合格。go還提供了一個 -coverprofile 參數,用來將覆蓋率相關的記錄輸出到文件 go test -cover -coverprofile=cover.out

PASS
coverage:?0.6%?of?statements
ok??	github.com/zhoushuguang/lebron/apps/product/rpc/internal/logic	1.459s

然后執行 go tool cover -html=cover.out,使用cover工具來處理生成的記錄信息,該命令會打開本地的瀏覽器窗口生成測試報告

解決依賴

對于單測中的依賴,我們一般采用mock的方式進行處理,gomock是Go官方提供的測試框架,它在內置的testing包或其他環境中都能夠很方便的使用。我們使用它對代碼中的那些接口類型進行mock,方便編寫單元測試。對于gomock的使用請參考gomock文檔

mock依賴interface,對于非interface場景下的依賴我們可以采用打樁的方式進行mock數據,monkey是一個Go單元測試中十分常用的打樁工具,它在運行時通過匯編語言重寫可執行文件,將目標函數或方法的實現跳轉到樁實現,其原理類似于熱補丁。monkey庫很強大,但是使用時需注意以下事項:

  • monkey不支持內聯函數,在測試的時候需要通過命令行參數-gcflags=-l關閉Go語言的內聯優化。
  • monkey不是線程安全的,所以不要把它用到并發的單元測試中。

其他

畫圖工具

社區中經常有人問畫圖用的是什么工具,本系列文章中的插圖工具主要是如下兩個

https://www.onemodel.app/

https://whimsical.com/

代碼規范

代碼不光是要實現功能,很重要的一點是代碼是寫給別人看的,所以我們對代碼的質量要有一定的要求,要遵循規范,可以參考go官方的代碼review建議

https://github.com/golang/go/wiki/CodeReviewComments

談談感受

時間過得賊快,不知不覺間這個系列已經寫到十一篇了。按照每周更新兩篇的速度也寫了一個多月了。寫文章是個體力活且非常的耗時,又生怕有寫的不對的地方,對大家產生誤導,所以還需要反復的檢查和查閱相關資料。平均一篇文章要寫一天左右,平時工作日比較忙,基本都是周六日來寫,因此最近一個月周六日基本沒有休息過。

但我覺得收獲也非常大,在寫文章的過程中,對于自己掌握的知識點,是一個復習的過程,可以讓自己加深對知識點的理解,對于自己沒有掌握的知識點就又是一個學習新知識的過程,讓自己掌握了新的知識,所以我和讀者也是一起在學習進步呢。大家都知道,對于自己理解的知識,想要說出來或者寫出來讓別人也理解也是不容易的,因此寫文章對自己的軟實力也是有很大的提升。

所以,我還是會繼續堅持寫文章,堅持輸出,和大家一起學習成長。同時,我也歡迎大家來 "微服務實踐" 公眾號來投稿??赡苡行┤擞X得自己的水平不行,擔心寫的內容不高端,沒有逼格,我覺得大可不必,只要能把知識點講明白就非常棒了,可以是基礎知識,也可以是最佳實踐等等。kevin會對投稿的每一篇文章都認真審核,寫的不對的地方他都會指出來,所有還有和kevin一對一交流學習的機會,小伙伴們抓緊行動起來呀。

結束語

非常感謝大家這一個多月以來的支持??吹矫科恼掠心敲炊嗟狞c贊,我十分的開心,也更加的有動力,所以,也在計劃寫下個系列的文章,目前有兩個待選的主題,分別是《go-zero源碼系列》和《gRPC實戰源碼系列》,歡迎小伙伴們在評論區留下你的評論,說出你更期待哪個系列,如果本篇文章點贊數超過66的話,咱就繼續開整。

代碼倉庫: https://github.com/zhoushuguang/lebron

項目地址

https://github.com/zeromicro/go-zero

https://gitee.com/kevwan/go-zero

歡迎使用 go-zerostar 支持我們!

微信交流群

關注『微服務實踐』公眾號并點擊 交流群 獲取社區群二維碼。

展開閱讀全文 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
  • 0
    感動
  • 0
    路過
  • 0
    高興
  • 0
    難過
  • 0
    搞笑
  • 0
    無聊
  • 0
    憤怒
  • 0
    同情
熱度排行
友情鏈接
18禁高潮出水呻吟娇喘mp3,日本熟妇乱人伦A片免费高清,成人午夜精品无码区,狠狠色噜噜色狠狠狠综合久久,麻豆一区二区99久久久久,年轻的妈妈4,少妇被又大又粗又爽毛片,护士张开腿让我爽了一夜,男男互攻互受h啪肉np文,你好神枪手电视剧免费观看啊,97人妻一区二区精品免费,久久久婷婷五月亚洲97号色,freegaysexvideos男男中国,国产精品国产三级国av麻豆,国产精品又黄又爽又色无遮挡网站,亚洲av无码一区二区三区网站,亚洲国产精品久久久久蜜桃,国产真人无码作爱视频免费,国产成人精品亚洲一区二区三区,亚洲欧洲日产最新,老司机带带我精彩免费,国产成人久久精品激情,日本最新av免费一区二区三区,边摸边吃奶又黄又激烈视频
<蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>