欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Golang 函數(shù)執(zhí)行時間統(tǒng)計裝飾器的一個實現(xiàn)詳解

 更新時間:2019年03月18日 10:00:09   作者:coordinate35  
這篇文章主要介紹了Golang 函數(shù)執(zhí)行時間統(tǒng)計裝飾器的一個實現(xiàn)詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

背景

最近在搭一個新項目的架子,在生產(chǎn)環(huán)境中,為了能實時的監(jiān)控程序的運行狀態(tài),少不了邏輯執(zhí)行時間長度的統(tǒng)計。時間統(tǒng)計這個功能實現(xiàn)的期望有下面幾點:

  1. 實現(xiàn)細節(jié)要剝離:時間統(tǒng)計實現(xiàn)的細節(jié)不期望在顯式的寫在主邏輯中。因為主邏輯中的其他邏輯和時間統(tǒng)計的抽象層次不在同一個層級
  2. 用于時間統(tǒng)計的代碼可復用
  3. 統(tǒng)計出來的時間結果是可被處理的。
  4. 對并發(fā)編程友好

實現(xiàn)思路

統(tǒng)計細節(jié)的剝離

最樸素的時間統(tǒng)計的實現(xiàn),可能是下面這個樣子:

func f() {
 startTime := time.Now()
 logicStepOne()
 logicStepTwo()
 endTime := time.Now()
 timeDiff := timeDiff(startTime, endTime)
 log.Info("time diff: %s", timeDiff)
}

《代碼整潔之道》告訴我們:一個函數(shù)里面的所有函數(shù)調用都應該處于同一個抽象層級。

在這里時間開始、結束的獲取,使用時間的求差,屬于時間統(tǒng)計的細節(jié),首先他不屬于主流程必要的一步,其次他們使用的函數(shù) time.Now() 和 logicStepOne, logicStepTwo 并不在同一個抽象層級。

因此比較好的做法應該是把時間統(tǒng)計放在函數(shù) f 的上層,比如:

func doFWithTimeRecord() {
 startTime: = time.Now()
 f()
 endTime := Time.Now()
 timeDiff := timeDIff(startTime, endTime)
 log.Info("time diff: %s", timeDiff)
}

時間統(tǒng)計代碼可復用&統(tǒng)計結果可被處理&不影響原函數(shù)的使用方式

我們雖然達成了函數(shù)內抽象層級相同的目標,但是大家肯定也能感受到:這個函數(shù)并不好用。

原因在于,我們把要調用的函數(shù) f 寫死在了 doFWithTimeRecord 函數(shù)中。這意味著,每一個要統(tǒng)計時間的函數(shù),我都需要實現(xiàn)一個 doXXWithTimeRecord, 而這些函數(shù)里面的邏輯是相同的,這就違反了我們 DRY(Don't Repeat Yourself)原則。因此為了實現(xiàn)邏輯的復用,我認為裝飾器是比較好的實現(xiàn)方式:將要執(zhí)行的函數(shù)作為參數(shù)傳入到時間統(tǒng)計函數(shù)中。

舉個網(wǎng)上看到的例子

實現(xiàn)一個功能,第一反應肯定是查找同行有沒有現(xiàn)成的輪子。不過看了下,沒有達到自己的期望,舉個例子:

type SumFunc func(int64, int64) int64

func timedSumFunc(f SumFunc) SumFunc {
 return func(start, end int64) int64 {
  defer func(t time.Time) {
   fmt.Printf("--- Time Elapsed: %v ---\n", time.Since(t))
  }(time.Now())
  
  return f(start, end)
 }
}

說說這段代碼不好的地方:

這個裝飾器入?yún)懰懒撕瘮?shù)的類型:

type SumFunc func(int64, int64) int64

也就是說,只要換一個函數(shù),這個裝飾器就不能用了,這不符合我們的第2點要求

這里時間統(tǒng)計結果直接打印到了標準輸出,也就是說這個結果是不能被原函數(shù)的調用方去使用的:因為只有掉用方,才知道這個結果符不符合預期,是花太多時間了,還是正?,F(xiàn)象。這不符合我們的第3點要求。

怎么解決這兩個問題呢?

這個時候,《重構,改善既有代碼的設計》告訴我們:Replace Method with Method Obejct——以函數(shù)對象取代函數(shù)。他的意思是當一個函數(shù)有比較復雜的臨時變量時,我們可以考慮將函數(shù)封裝成一個類。這樣我們的函數(shù)就統(tǒng)一成了 0 個參數(shù)。(當然,原本就是作為一個 struct 里面的方法的話就適當做調整就好了)

現(xiàn)在,我們的代碼變成了這樣:

type TimeRecorder interface {
 SetCost(time.Duration)
 TimeCost() time.Duration
}

func TimeCostDecorator(rec TimeRecorder, f func()) func() {
 return func() {
  startTime := time.Now()
  f()
  endTime := time.Now()
  timeCost := endTime.Sub(startTime)
  rec.SetCost(timeCost)
 }
}

這里入?yún)懗墒且粋€ interface ,目的是允許各種函數(shù)對象入?yún)ⅲ恍枰獙崿F(xiàn)了 SetCost 和 TimeCost 方法即可

對并發(fā)編程友好

最后需要考慮的一個問題,很多時候,一個類在整個程序的生命周期是一個單例,這樣在 SetCost 的時候,就需要考慮并發(fā)寫的問題。這里考慮一下幾種解決方案:

使用裝飾器配套的時間統(tǒng)計存儲對象,實現(xiàn)如下:

func NewTimeRecorder() TimeRecorder {
 return &timeRecorder{}
}

type timeRecorder struct {
 cost time.Duration
}

func (tr *timeRecorder) SetCost(cost time.Duration) {
 tr.cost = cost
}

func (tr *timeRecorder) Cost() time.Duration {
 return tr.cost
}

抽離出存粹的執(zhí)行完就可以銷毀的函數(shù)對象,每次要操作的時候都 new 一下

函數(shù)對象內部對 SetCost 函數(shù)實現(xiàn)鎖機制

這三個方案是按推薦指數(shù)從高到低排序的,因為我個人認為:資源允許的情況下,盡量保持對象不可變;同時怎么統(tǒng)計、存儲使用時長其實是統(tǒng)計時間模塊自己的事情。

單元測試

最后補上單元測試:

func TestTimeCostDecorator(t *testing.T) {
 testFunc := func() {
  time.Sleep(time.Duration(1) * time.Second)
 }
 
 type args struct {
  rec TimeRecorder
  f func()
 }
 
 tests := []struct {
  name string
  args args
 }{
  {
   "test time cost decorator",
   args{
    NewTimeRecorder(),
    testFunc,
   },
  },
 }
 for _, tt := range tests {
  t.Run(tt.name, func(t *testing.T) {
   got := TimeCostDecorator(tt.args.rec, tt.args.f)
   got()
   if tt.args.rec.Cost().Round(time.Second) != time.Duration(1) * time.Second.Round(time.Second) {
    "Record time cost abnormal, recorded cost: %s, real cost: %s",
    tt.args.rec.Cost().String(),
    tt.Duration(1) * time.Second,
   }
  }) 
 }
}

測試通過,驗證了時間統(tǒng)計是沒問題的。至此,這個時間統(tǒng)計裝飾器就介紹完了。如果這個實現(xiàn)有什么問題,或者大家有更好的實現(xiàn)方式,歡迎大家批評指正與提出~

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • 詳解Go語言Sync.Pool為何不加鎖也能夠實現(xiàn)線程安全

    詳解Go語言Sync.Pool為何不加鎖也能夠實現(xiàn)線程安全

    在這篇文章中,我們將剖析sync.Pool內部實現(xiàn)中,介紹了sync.Pool比較巧妙的內部設計思路以及其實現(xiàn)方式。在這個過程中,也間接介紹了為何不加鎖也能夠實現(xiàn)線程安全,感興趣的可以學習一下
    2023-04-04
  • Golang?Compare?And?Swap算法詳細介紹

    Golang?Compare?And?Swap算法詳細介紹

    CAS算法是一種有名的無鎖算法。無鎖編程,即不使用鎖的情況下實現(xiàn)多線程之間的變量同步,也就是在沒有線程被阻塞的情況下實現(xiàn)變量的同步,所以也叫非阻塞同步Non-blocking?Synchronization
    2022-10-10
  • Go語言函數(shù)學習教程

    Go語言函數(shù)學習教程

    這篇文章主要介紹了Go語言函數(shù)基本用法,結合實例形式分析了Go語言函數(shù)的格式、定義、使用方法與相關注意事項,需要的朋友可以參考下
    2016-07-07
  • Gin框架使用panic處理中間件問題詳解

    Gin框架使用panic處理中間件問題詳解

    這篇文章主要介紹了Gin框架使用panic處理中間件問題,在 Gin 框架中,錯誤處理和 panic 處理是非常重要的功能。當處理 HTTP 請求時,可能會出現(xiàn)各種各樣的錯誤,例如數(shù)據(jù)庫連接錯誤、網(wǎng)絡錯誤、權限問題等等
    2023-04-04
  • 使用?pprof?進行性能分析的方法詳解

    使用?pprof?進行性能分析的方法詳解

    pprof?是?Go?語言中用于性能分析的一個強大工具,它可以幫助開發(fā)人員找到應用程序中的性能瓶頸,并提供詳細的分析報告,本文將介紹如何使用?pprof?進行性能分析,需要的朋友可以參考下
    2023-05-05
  • go:垃圾回收GC觸發(fā)條件詳解

    go:垃圾回收GC觸發(fā)條件詳解

    這篇文章主要介紹了go:垃圾回收GC觸發(fā)條件詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • Golang實現(xiàn)斷點續(xù)傳功能

    Golang實現(xiàn)斷點續(xù)傳功能

    這篇文章主要為大家詳細介紹了Golang實現(xiàn)斷點續(xù)傳、復制文件功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • 淺談go中cgo的幾種使用方式

    淺談go中cgo的幾種使用方式

    本文主要介紹了淺談go中cgo的幾種使用方式,文中根據(jù)實例編碼詳細介紹的十分詳盡,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Go語言自定義linter靜態(tài)檢查工具

    Go語言自定義linter靜態(tài)檢查工具

    這篇文章主要介紹了Go語言自定義linter靜態(tài)檢查工具,Go語言是一門編譯型語言,編譯器將高級語言翻譯成機器語言,會先對源代碼做詞法分析,詞法分析是將字符序列轉換為Token序列的過程,文章詳細介紹需要的小伙伴可以參考一下
    2022-05-05
  • Go語言實現(xiàn)二維數(shù)組的2種遍歷方式以及案例詳解

    Go語言實現(xiàn)二維數(shù)組的2種遍歷方式以及案例詳解

    這篇文章主要介紹了Go語言實現(xiàn)二維數(shù)組的2種遍歷方式以及案例詳解,圖文代碼聲情并茂,有感興趣的可以學習下
    2021-03-03

最新評論