<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 8274字,需瀏覽 17分鐘

           ·

          2022-08-04 09:49

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

          內容回顧

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

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

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

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

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

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

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

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

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

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

          go-zero微服務實戰(zhàn)系列(九、極致優(yōu)化秒殺性能)

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

          單元測試

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

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

          go test工具

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

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

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

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

          單測格式

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

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

          測試函數(shù)的名字必須以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的函數(shù),函數(shù)如下:

          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
          }

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

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

          在當前路徑下執(zhí)行 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

          在執(zhí)行 go test 命令的時候可以添加 -run 參數(shù),它對應一個正則表達式,又有函數(shù)名匹配上的測試函數(shù)才會被 go test 命令執(zhí)行,例如我們可以使用 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)
             }
            })
           }
          }

          執(zhí)行命令 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語言又天生支持并發(fā),所以很容易發(fā)揮自身優(yōu)勢將表格驅動測試并行化,可以通過t.Parallel() 來實現(xiàn):

          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 參數(shù),用來將覆蓋率相關的記錄輸出到文件 go test -cover -coverprofile=cover.out

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

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

          解決依賴

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

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

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

          其他

          畫圖工具

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

          https://www.onemodel.app/

          https://whimsical.com/

          代碼規(guī)范

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

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

          談談感受

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

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

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

          結束語

          非常感謝大家這一個多月以來的支持。看到每篇文章有那么多的點贊,我十分的開心,也更加的有動力,所以,也在計劃寫下個系列的文章,目前有兩個待選的主題,分別是《go-zero源碼系列》和《gRPC實戰(zhàn)源碼系列》,歡迎小伙伴們在評論區(qū)留下你的評論,說出你更期待哪個系列,如果本篇文章點贊數(shù)超過66的話,咱就繼續(xù)開整。

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



          推薦閱讀


          福利

          我為大家整理了一份從入門到進階的Go學習資料禮包,包含學習建議:入門看什么,進階看什么。關注公眾號 「polarisxu」,回復 ebook 獲取;還可以回復「進群」,和數(shù)萬 Gopher 交流學習。

          瀏覽 35
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国语对白永久免费 | 91精选久久 | 97久久一区二区三区 | 国产精品一卡2卡3卡4卡5卡免费网站 | 美女逼视频 |