Middleware in Golang

作者: xpzouying | 来源:发表于2017-12-13 17:11 被阅读99次

有时候我们需要统计web service接口的数据,比如记录日志、统计API调用时间、或者对HandleFunc进行错误处理,

这个时候,middleware就很有帮助。

最初版本

我们目前有个程序,监听8080端口,提供两个接口,

1. compute:进行计算,耗时在500ms-1000ms之间
2. version:得到版本号,耗时在10ms-60ms之间
package main

import (
    "log"
    "math/rand"
    "net/http"
    "time"
)

// Compute is compute process for doing task
func Compute(w http.ResponseWriter, r *http.Request) {
    time.Sleep(time.Duration((500 + rand.Intn(500))) * time.Millisecond)
}


// Version is getting version task
func Version(w http.ResponseWriter, r *http.Request) {
    time.Sleep(time.Duration((10 + rand.Intn(50))) * time.Millisecond)
    w.Write([]byte("version"))
}

func main() {
    http.HandleFunc("/compute", Compute)
    http.HandleFunc("/version", Version)

    log.Panic(http.ListenAndServe(":8080", nil))
}

调用结果如下,

# curl http://localhost:8080/compute
finish

增加日志

需要记录每次调用接口的名字。

有两种方式记录日志,

1. 在每一个HandleFunc第一行输出URI
2. 增加中间件封装HandleFunc

对于第一种情况,修改Compute()函数为,

// Compute is compute process for doing task
func Compute(w http.ResponseWriter, r *http.Request) {
    log.Printf(r.RequestURI)

    time.Sleep(time.Duration((500 + rand.Intn(500))) * time.Millisecond)
    w.Write([]byte("finish"))
}

运行得到结果为,

2017/12/11 23:07:24 /compute

对于API少的情况,还比较适用。但是对于API接口比较多的情况,修改每一个函数就不太合适。并且当我们不光想统计URI信息时,还需要统计每一个调用接口的其他信息时,就需要修改每一处HandleFunc。

所以我们可以使用Middleware形式来完成。

我们希望使用middlware以后,对于main()函数变为,

func main() {
    http.HandleFunc("/compute", middleware(Compute))
    http.HandleFunc("/version", middleware(Version))

    log.Panic(http.ListenAndServe(":8080", nil))
}

使用middlware对每一个http handle func进行封装,在middleware中进行相应的需求处理,比如日志记录、错误处理、用时统计等。

func middleware(fn func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        begin := time.Now()
        defer func() {
            log.Printf("[%s] %s, time_used: %v", r.Method, r.URL.String(), time.Now().Sub(begin))
        }()

        fn(w, r)
    }
}

middlware入参和返回值都为func(w http.ResponseWriter, r *http.Request)类型。

middleware中,进行了调用接口URI的统计和用时统计。

完整的代码如下,

package main

import (
    "log"
    "math/rand"
    "net/http"
    "time"
)

// Compute is compute process for doing task
func Compute(w http.ResponseWriter, r *http.Request) {
    time.Sleep(time.Duration((500 + rand.Intn(500))) * time.Millisecond)
    w.Write([]byte("finish"))
}

// Version is getting version task
func Version(w http.ResponseWriter, r *http.Request) {
    time.Sleep(time.Duration((10 + rand.Intn(50))) * time.Millisecond)
    w.Write([]byte("version"))
}

func middleware(fn func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        begin := time.Now()
        defer func() {
            log.Printf("[%s] %s, time_used: %v", r.Method, r.URL.String(), time.Now().Sub(begin))
        }()

        fn(w, r)
    }
}

func main() {
    http.HandleFunc("/compute", middleware(Compute))
    http.HandleFunc("/version", middleware(Version))

    log.Panic(http.ListenAndServe(":8080", nil))
}

测试

curl http://localhost:8080/compute
# 2017/12/13 16:57:15 [GET] /compute, time_used: 583.357048ms

curl http://localhost:8080/version
# 2017/12/13 16:57:24 [GET] /version, time_used: 47.09209ms

Source code in github.com

相关文章

网友评论

    本文标题:Middleware in Golang

    本文链接:https://www.haomeiwen.com/subject/vsrzixtx.html