最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

背景

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

  1. 實(shí)現(xiàn)細(xì)節(jié)要?jiǎng)冸x:時(shí)間統(tǒng)計(jì)實(shí)現(xiàn)的細(xì)節(jié)不期望在顯式的寫在主邏輯中。因?yàn)橹鬟壿嬛械钠渌壿嫼蜁r(shí)間統(tǒng)計(jì)的抽象層次不在同一個(gè)層級(jí)
  2. 用于時(shí)間統(tǒng)計(jì)的代碼可復(fù)用
  3. 統(tǒng)計(jì)出來(lái)的時(shí)間結(jié)果是可被處理的。
  4. 對(duì)并發(fā)編程友好

實(shí)現(xiàn)思路

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

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

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

《代碼整潔之道》告訴我們:一個(gè)函數(shù)里面的所有函數(shù)調(diào)用都應(yīng)該處于同一個(gè)抽象層級(jí)。

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

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

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

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

我們雖然達(dá)成了函數(shù)內(nèi)抽象層級(jí)相同的目標(biāo),但是大家肯定也能感受到:這個(gè)函數(shù)并不好用。

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

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

實(shí)現(xiàn)一個(gè)功能,第一反應(yīng)肯定是查找同行有沒(méi)有現(xiàn)成的輪子。不過(guò)看了下,沒(méi)有達(dá)到自己的期望,舉個(gè)例子:

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)
 }
}

說(shuō)說(shuō)這段代碼不好的地方:

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

type SumFunc func(int64, int64) int64

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

這里時(shí)間統(tǒng)計(jì)結(jié)果直接打印到了標(biāo)準(zhǔn)輸出,也就是說(shuō)這個(gè)結(jié)果是不能被原函數(shù)的調(diào)用方去使用的:因?yàn)橹挥械粲梅剑胖肋@個(gè)結(jié)果符不符合預(yù)期,是花太多時(shí)間了,還是正?,F(xiàn)象。這不符合我們的第3點(diǎn)要求。

怎么解決這兩個(gè)問(wèn)題呢?

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

現(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)懗墒且粋€(gè) interface ,目的是允許各種函數(shù)對(duì)象入?yún)?,只需要?shí)現(xiàn)了 SetCost 和 TimeCost 方法即可

對(duì)并發(fā)編程友好

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

使用裝飾器配套的時(shí)間統(tǒng)計(jì)存儲(chǔ)對(duì)象,實(shí)現(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ù)對(duì)象,每次要操作的時(shí)候都 new 一下

函數(shù)對(duì)象內(nèi)部對(duì) SetCost 函數(shù)實(shí)現(xiàn)鎖機(jī)制

這三個(gè)方案是按推薦指數(shù)從高到低排序的,因?yàn)槲覀€(gè)人認(rèn)為:資源允許的情況下,盡量保持對(duì)象不可變;同時(shí)怎么統(tǒng)計(jì)、存儲(chǔ)使用時(shí)長(zhǎng)其實(shí)是統(tǒng)計(jì)時(shí)間模塊自己的事情。

單元測(cè)試

最后補(bǔ)上單元測(cè)試:

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,
   }
  }) 
 }
}

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

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 詳解Go語(yǔ)言中for range的

    詳解Go語(yǔ)言中for range的"坑"

    這篇文章主要介紹了詳解Go語(yǔ)言中for range的"坑",文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Go defer使用時(shí)的兩個(gè)常見陷阱與避免方法

    Go defer使用時(shí)的兩個(gè)常見陷阱與避免方法

    這篇文章主要將帶大家一起深入探討 Go 1.20 中 defer 的優(yōu)化機(jī)制,并揭示在使用 defer 時(shí)需要避免的兩個(gè)常見陷阱,有需要的可以了解下
    2025-03-03
  • GoLang讀取文件的10種方法實(shí)例

    GoLang讀取文件的10種方法實(shí)例

    讀取文件是所有編程語(yǔ)言中最常見的操作之一,下面這篇文章主要給大家介紹了關(guān)于GoLang讀取文件的10種方法,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-06-06
  • Go?gRPC服務(wù)端流式RPC教程示例

    Go?gRPC服務(wù)端流式RPC教程示例

    這篇文章主要為大家介紹了Go?gRPC服務(wù)端流式RPC教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • Go語(yǔ)言錯(cuò)誤處理異常捕獲+異常拋出

    Go語(yǔ)言錯(cuò)誤處理異常捕獲+異常拋出

    這篇文章主要介紹了Go語(yǔ)言錯(cuò)誤處理異常捕獲和異常拋出,Go語(yǔ)言的作者認(rèn)為java等語(yǔ)言的錯(cuò)誤處理底層實(shí)現(xiàn)較為復(fù)雜,就實(shí)現(xiàn)了函數(shù)可以返回錯(cuò)誤類型以及簡(jiǎn)單的異常捕獲,雖然簡(jiǎn)單但是也非常精妙,大大的提高了運(yùn)行效率,下文需要的朋友可以參考一下
    2022-02-02
  • Go并發(fā)之RWMutex的源碼解析詳解

    Go并發(fā)之RWMutex的源碼解析詳解

    RWMutex是一個(gè)支持并行讀串行寫的讀寫鎖。RWMutex具有寫操作優(yōu)先的特點(diǎn),寫操作發(fā)生時(shí),僅允許正在執(zhí)行的讀操作執(zhí)行,后續(xù)的讀操作都會(huì)被阻塞。本文就來(lái)從源碼解析一下RWMutex的使用
    2023-03-03
  • Go設(shè)計(jì)模式之單例模式講解和代碼示例

    Go設(shè)計(jì)模式之單例模式講解和代碼示例

    單例是一種創(chuàng)建型設(shè)計(jì)模式,讓你能夠保證一個(gè)類只有一個(gè)實(shí)例,并提供一個(gè)訪問(wèn)該實(shí)例的全局節(jié)點(diǎn),本文就通過(guò)代碼示例給大家講講Go單例模式,需要的朋友可以參考下
    2023-07-07
  • 看看你的Go應(yīng)用是否用了正確CPU核數(shù)

    看看你的Go應(yīng)用是否用了正確CPU核數(shù)

    這篇文章主要為大家介紹了Go應(yīng)用正確的CPU核數(shù)分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-06-06
  • go語(yǔ)言區(qū)塊鏈學(xué)習(xí)調(diào)用以太坊

    go語(yǔ)言區(qū)塊鏈學(xué)習(xí)調(diào)用以太坊

    這篇文章主要為大家介紹了go語(yǔ)言區(qū)塊鏈學(xué)習(xí)如何調(diào)用以太坊的示例實(shí)現(xiàn)過(guò)程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2021-10-10
  • Go語(yǔ)言常用字符串處理方法實(shí)例匯總

    Go語(yǔ)言常用字符串處理方法實(shí)例匯總

    這篇文章主要介紹了Go語(yǔ)言常用字符串處理方法,實(shí)例匯總了Go語(yǔ)言中常見的各種字符串處理技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-03-03

最新評(píng)論

黑河市| 拜泉县| 庆元县| 修水县| 瑞金市| 定陶县| 永新县| 黑山县| 玉屏| 建瓯市| 麟游县| 阳春市| 宜黄县| 昌黎县| 宕昌县| 曲松县| 周宁县| 奇台县| 华容县| 桦甸市| 仲巴县| 茶陵县| 习水县| 富裕县| 得荣县| 阿尔山市| 石林| 桂林市| 赞皇县| 安乡县| 裕民县| 建平县| 罗田县| 凤翔县| 扶余县| 延津县| 什邡市| 沙田区| 卓资县| 修水县| 深圳市|