前提概要
承接上一回【Golang | Web】构建Web服务器,我们实现了一个简易的应用HelloWorld,通过监听本地8080端口,提供web服务。客户端通过访问http://127.0.0.1:8080/可以获取返回值 “Hello World!”
故事背景
现在服务端想要统计每次响应请求消耗的时间(这里只涉及app处理请求消耗的时间,不包含链路通信等其他时间),我们可以对HelloWorld作如下简单修改
func HelloWorld(w http.ResponseWriter, r *http.Request) {
timeStart := time.Now()
_, err := fmt.Fprintf(w, "Hello World!")
if err != nil {
log.Panic(err)
}
timeElapse := time.Since(timeStart)
log.Println(timeElapse)
}
现在如果有第二个,第三个应用,我们也可以进行简单的代码添加。但如果应用很多,30个、100个,这种方法显然就不行了。问题的关键就在于业务代码和非业务代码(时间统计)夹杂在一起了
中间件的引入
思路很简单,就是服务器将获取到的request请求先传递给时间统计的中间件timeMiddleware,然后再由中间件传递给应用HelloWorld,下面是timeMiddleware简单的实现
func timeMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
timeStart := time.Now()
//传递参数给下一个http.Handler
next.ServeHTTP(w, r)
timeElapse := time.Since(timeStart)
log.Println(timeElapse)
})
}
在main函数中开启监听
func main() {
http.Handle("/", timeMiddleware(http.HandlerFunc(HelloWorld)))
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Panic(err)
}
}
这里需要补充说明http.Handler、http.HandlerFunc、ServeHttp之间的关系
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
Handler是一个接口,只有一个方法ServeHttp,HandlerFunc是一个函数类型,实现了Handler接口,所以在timeMiddleware中通过调用next.ServeHTTP(w, r) 就是在传递参数执行app
现在拓展一下,如果要继续添加一个log功能,我们可以这么实现
//新增一个中间件用于log的生成
func logMiddleware(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
file, _ := os.OpenFile("test.log", os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
logger := log.New(file, "", log.Llongfile|log.LstdFlags)
//将w,r传递给下一个handler
h.ServeHTTP(w, r)
logger.Println("请求处理完成")
})
}
func main() {
http.Handle("/", logMiddleware(timeMiddleware(http.HandlerFunc(HelloWorld))))
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Panic(err)
}
}
通过浏览器访问后,后台可以获取如下的log信息:
2022/04/16 19:32:07 E:/goland-workspace/goRestful/httptest/main.go:38: 请求处理完成
2022/04/16 19:32:07 E:/goland-workspace/goRestful/httptest/main.go:38: 请求处理完成