计算在Go中调用(请求处理程序)函数的次数

时间:2014-03-26 00:53:04

标签: http debugging go

上下文

我正在制作一个提供动态生成的pdf的网络应用。这些内容包含来自互联网的内容,因此每次它提供pdf时,它都会将一些文件下载到新的临时文件夹中。

问题

在我加载页面一次后,我最终得到了大量的文件夹,所以看起来,由于某种原因,处理程序被多次调用,这是一个问题因为我多次下载多次比我需要的不是非实质性文件。我想检查流程的哪个阶段正在发生多个请求。

问题

有没有办法计算一个函数被调用了多少次,很可能使用闭包?(我还没有完全关闭我的心理模型进行编程;我不完全理解他们/他们如何使用)。 这最好是涉及语言中的int,而不是在每个阶段打印一些东西并手工计数 - 我正在寻找一种比这更具可扩展性的解决方案(对于以后的情况以及这种情况)。

谢谢!

2 个答案:

答案 0 :(得分:2)

以下是两种计算函数调用的方法,一种用于方法调用。还有很多其他方法,但只是为了让你开始:

使用闭包(不是我推荐的)

package main

import(
    "fmt"
    "sync/atomic"
)

var Foo = func() (func() uint64) {
    var called uint64
    return func() uint64 {
        atomic.AddUint64(&called, 1)
        fmt.Println("Foo!")
        return called
    }
}()

func main() {
    Foo()
    c := Foo()
    fmt.Printf("Foo() is called %d times\n", c)
}

游乐场:http://play.golang.org/p/euKbamdI7h

使用全局计数器:

package main

import (
    "fmt"
    "sync/atomic"
)

var called uint64

func Foo() {
    atomic.AddUint64(&called, 1)
    fmt.Println("Foo!");
}

func main() {
    Foo()
    Foo()
    fmt.Printf("Foo() is called %d times\n", called)
}

游乐场:http://play.golang.org/p/3Ib29VCnoF

计数方法调用:

package main

import (
    "fmt"
    "sync/atomic"
)

type T struct {
    Called uint64
}

func (t *T) Foo() {
    atomic.AddUint64(&t.Called, 1)
    fmt.Println("Foo!")
}

func main() {
    var obj T
    obj.Foo()
    obj.Foo()
    fmt.Printf("obj.Foo() is called %d times\n", obj.Called)
}

游乐场:http://play.golang.org/p/59eOQdUQU1

修改

我刚刚意识到处理程序可能不在你自己的包中。在这种情况下,您可能想要编写一个包装器:

var called uint64

func Foo() {
    atomic.AddUint64(&called, 1)
    importedPackage.Foo()
}

编辑2:

更新了使用原子+1操作的示例。

答案 1 :(得分:1)

点击通话

要回答您提出的具体问题,这里有一种快速计算处理程序执行的方法:

func countCalls(h http.HandlerFunc) http.HandlerFunc {
    var lock sync.Mutex
    var count int
    return func(w http.ResponseWriter, r *http.Request) {
        lock.Lock()
        count++
        w.Header().Set("X-Call-Count", fmt.Sprintf("%d", count))
        lock.Unlock()

        h.ServeHTTP(w, r)
    }
}

http.Handle("/foobar", countCalls(foobarHandler))

这将添加一个标题,您可以使用自己喜欢的Web开发人员工具进行检查;您也可以将其记录到标准输出或其他东西。

记录处理程序

为了扩展上面提到的答案,你可能想要做的就是调试这个并准备好以备将来使用的是记录每个请求的细节。

package main

import (
    "flag"
    "log"
    "net/http"
    "os"

    "github.com/gorilla/handlers"
)

var (
    accessLogFile = flag.String("log", "/var/log/yourapp/access.log", "Access log file")
)

func main() {
    accessLog, err := os.OpenFile(*accessLogFile, os.O_CREATE|os.O_WRITE|os.O_APPEND, 0644)
    if err != nil {
        log.Fatalf("Failed to open access log: %s", err)
    }

    wrap := func(f http.HandlerFunc) http.Handler {
        return handlers.LoggingHandler(accessLog, http.HandlerFunc(foobarHandler))
    }

    http.Handle("/foobar", wrap(foobarHandler))

    ...
}

这使用LoggingHandler(或CombinedLoggingHandler)来编写标准Apache format日志消息,您可以自行检查或使用各种工具进行分析。

日志行的一个例子是

127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326

告诉您发出请求的人,时间,方法和URL的内容,服务器的响应方式以及响应时间。从这个日志中,您应该能够确切地看到正在进行的请求,不仅要确定调用处理程序的次数,还要确定生成请求的确切内容以及它们是否属于另一个端点(如{{1 }})。