如何实现Golang函数执行时间统计装饰器

  介绍

小编给大家分享一下如何实现Golang函数执行时间统计装饰器,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获、下面让我们一起去了解一下吧!

<强>背景

最近在搭一个新项目的架子,在生产环境中,为了能实时的监控程序的运行状态,少不了逻辑执行时间长度的统计。时间统计这个功能实现的期望有下面几点:

<李>

实现细节要剥离:时间统计实现的细节不期望在显式的写在主逻辑中。因为主逻辑中的其他逻辑和时间统计的抽象层次不在同一个层级

<李>

用于时间统计的代码可复用

<李>

统计出来的时间结果是可被处理的。

<李>

对并发编程友好

<强>实现思路

<强>统计细节的剥离

最朴素的时间统计的实现,可能是下面这个样子:

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

《代码整洁之道》告诉我们:一个函数里面的所有函数调用都应该处于同一个抽象层级。

在这里时间开始,结束的获取,使用时间的求差,属于时间统计的细节,首先他不属于主流程必要的一步,其次他们使用的函数time.Now()和logicStepOne, logicStepTwo并不在同一个抽象层级。

因此比较好的做法应该是把时间统计放在函数f的上层,比如:

func  doFWithTimeRecord (), {   ,开始时间:=,time.Now ()   ,f ()   ,endTime :=, Time.Now ()   ,timeDiff :=, timeDIff(开始时间,endTime)   ,log.Info (“time  diff: % s",, timeDiff)   }

<>强时间统计代码可复用,统计结果可被处理,不影响原函数的使用方式

我们虽然达成了函数内抽象层级相同的目标,但是大家肯定也能感受到:这个函数并不好用。

原因在于,我们把要调用的函数f写死在了doFWithTimeRecord函数中。这意味着,每一个要统计时间的函数,我都需要实现一个doXXWithTimeRecord,而这些函数里面的逻辑是相同的,这就违反了我们干(不要# 39;t重复自己)原则。因此为了实现逻辑的复用,我认为装饰器是比较好的实现方式:将要执行的函数作为参数传入到时间统计函数中。

<强>举个网上看到的例子

实现一个功能,第一反应肯定是查找同行有没有现成的轮子。不过看了下,没有达到自己的期望,举个例子:

type  SumFunc  func (int64, int64), int64      func  timedSumFunc (f  SumFunc), SumFunc  {   ,return  func(开始,最终获得int64), int64  {   defer 才能;func (t  time.Time), {   ,,fmt.Printf(“——安康;Time 运行:,% v  - - - - - - \ n",, time.Since (t))   ,,}(time.Now ())   ,,   return 才能;f(开始,结束)   ,}   }

说说这段代码不好的地方:

这个装饰器入参写死了函数的类型:

type  SumFunc  func (int64, int64), int64

也就是说,只要换一个函数,这个装饰器就不能用了,这不符合我们的第2点要求

这里时间统计结果直接打印到了标准输出,也就是说这个结果是不能被原函数的调用方去使用的:因为只有掉用方,才知道这个结果符不符合预期,是花太多时间了,还是正常现象。这不符合我们的第3点要求。

<强>怎么解决这两个问题呢?

这个时候,《重构,改善既有代码的设计》告诉我们:替换法方法Obejct——以函数对象取代函数。他的意思是当一个函数有比较复杂的临时变量时,我们可以考虑将函数封装成一个类。这样我们的函数就统一成了0个参数。(当然,原本就是作为一个结构体里面的方法的话就适当做调整就好了)

现在,我们的代码变成了这样:

type  TimeRecorder  interface  {   ,SetCost (time.Duration)   ,时间成本()time.Duration   }      func  TimeCostDecorator (rec 时间记录,f  func ()), func (), {   ,return  func (), {   startTime 才能;:=,time.Now ()   f()才能   endTime 才能;:=,time.Now ()   timeCost 才能;:=,endTime.Sub(开始时间)   rec.SetCost才能(时间成本)   ,}   }

这里入参写成是一个接口,目的是允许各种函数对象入参,只需要实现了SetCost和时间成本方法即可

如何实现Golang函数执行时间统计装饰器