英文:
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
).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论