在Go语言中,计算一个(请求处理)函数被调用的次数的方法是:

huangapple go评论94阅读模式
英文:

Working out the number of times a (request handler) function has been called in Go

问题

上下文

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

问题

在加载页面一次后,我会得到大量的文件夹,所以似乎处理程序被调用了多次,这是一个问题,因为我下载了比我需要的多得多的文件。我想知道在这个过程的哪个阶段发生了多次请求。

问题

有没有办法通过闭包等方式来确定一个函数被调用的次数?(我还没有完全理解闭包以及它们的使用方式)。最好是使用语言中的整数来实现,而不是在每个阶段打印一些内容并手动计数 - 我正在寻找比这更可扩展的解决方案(适用于以后的情况以及当前的情况)。

谢谢!

英文:

Context

I'm making a web app that serves dynamically generated pdfs. These contain content from the internet, so every time it serves a pdf, it downloads a number of files to a new temporary folder.

The Problem

I end up with a large number of folders after I load the page once, so it seems that, for some reason, the handler is being called multiple times, which is an issue because I'm downloading multiple times more than I need to of not insubstantial files. I'd like to check at what stage of the process multiple requests are occurring.

The Question

Is there a way of working out how many times a function has been called, quite possibly using closures? (I haven't quite got closures into my mental model for programming yet; I don't completely understand them/how they're used).
This would preferably be something involving an int in the language rather than printing something at every stage and counting by hand - I'm looking for a more scalable solution than that (for later situations as well as this one).

Thanks!

答案1

得分: 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() 被调用了 %d 次\n", c)
}

Playground: 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() 被调用了 %d 次\n", called)
}

Playground: 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() 被调用了 %d 次\n", obj.Called)
}

Playground: http://play.golang.org/p/59eOQdUQU1

编辑:

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

var called uint64

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

编辑2:

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

英文:

Here are two ways you can count function calls, and one for method calls. There are plenty of other ways too, but just to get you started:

Using closure: (not what I would recommended)

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

Playground: http://play.golang.org/p/euKbamdI7h

Using global counter:

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

Playground: http://play.golang.org/p/3Ib29VCnoF

Counting method calls:

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

Playground: http://play.golang.org/p/59eOQdUQU1

Edit:

I just realized that the handler might not be in your own package. In such a case, you might want to write a wrapper:

var called uint64

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

Edit 2:

Updated the examples to use atomic +1 operations.

答案2

得分: 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))

这将添加一个头部,你可以使用你喜欢的网络开发工具进行检查;你也可以将其记录到标准输出或其他地方。

记录处理程序

为了进一步扩展上面提到的答案,你可能想要做的是记录每个请求的详细信息,以便调试并在将来使用。

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_WRONLY|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 格式 日志消息,你可以自己检查或使用各种工具进行分析。

日志行的一个示例可能是:

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

它告诉你谁发出了请求、何时发出的、方法和 URL 是什么、你的服务器如何响应以及响应的长度。通过这个日志,你应该能够看到确切的请求是什么,不仅可以确定处理程序被调用的次数,还可以确定是什么生成了这些请求,以及它们是否是发往另一个端点(比如 /favicon.ico)。

英文:

Counting Calls

To answer the specific question you asked, here is one quick way to count handler executions:

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

This will add a header that you can inspect with your favorite web developer tools; you could also just log it to standard output or something.

Logging Handlers

To expand upon the answers mentioned above, what you probably want to do to debug this and have in place for future use is to log details of each request.

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

    ...
}

This uses LoggingHandler (or CombinedLoggingHandler) to write a standard Apache format log message that you can either inspect yourself or analyze with various tools.

An example of a log line would be

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

which tells you who made the request, when, what the method and URL was, how your server responded, and how long the response was. From this log, you should be able to see exactly what requests are being made, to determine not only how many times your handlers are being called, but exactly what is generating the requests and whether they're to another endpoint (like /favicon.ico).

huangapple
  • 本文由 发表于 2014年3月26日 08:53:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/22649395.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定