• Go语言中间件使用方法以及原理解析


    什么是中间件?
    在网络世界中,中间件针对客户端发起的网络请求而执行的代码,并且将请求链接到下一个中间件,最终到达目标处理函数。当您希望对多个不同请求做一些共享的操作,中间件是很有用的。例如对网络请求进行身份验证、性能日志记录和数据收集。
    举一个例子,假如我们有一个服务返回用户提交的消息列表,另一个服务是返回关于该用户的个人敏感信息。只有当用户登录到服务后,才能访问这两个服务。此外,我们还希望记录客户端向服务器发出的每个请求的执行时间。身份验证和日志记录的代码可以与控制器一起编写,并复制到两个服务即可。但是这个例子很容易被分成四个不同的包来管理。一个记录请求数据(LOG),一个验证用户是否登录(AUTH),一个返回消息列表(RETURN MSG),和一个返回用户数据(RETURN USER)。
    通过将这些功能作为中间件来编写,我们可以很容易将请求连接起来,并在需要时在每个中间件上提前返回。例如,用户没有登录就发送请求。整个处理过程可能是如下方式:
    [LOG] -> [AUTH] -> [RETURN MSG]
    [LOG] -> [AUTH] -> [RETURN USER]
    由于分离了LOG和AUTH,我们只需要编写代码和测试一次就可以。
    Go中间件
    在Go中,所有的网络请求都通过实现net/http包中Handler接口来完成。对不太熟悉Go的开发人员来说,Handler是响应请求并处理对请求参数的读取和响应的写入。因为Go在其关键函数中都需要这个接口,所以围绕它构建包或者装饰器来扩展接口是很容易并且可靠的。
    创建中间件的快速方法就是封装net/http.Handler.ServeHTTP方法:
    ServeHTTP(ResponseWriter, *Request)
    通过添加一个外部函数,该函数接收一个Handler类型参数,并使用HandlerFunc函数来返回一个Handler:
    func middleware(next http.Handler) http.Handler {
      return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Code for the middleware...
        next.ServeHTTP(w, r)
      })
    }
    上面的函数接收一个Handler类型的参数,该参数将请求链接到最终的函数上去。因为返回值也是Handler类型,可以将调用串起来:
    firstMiddleware(secondMiddleware(lastHandler))
    仅通过这个简单的方法,就可以很好地实现中间件。虽然不是很健壮,但是可行的。
    接下来,我将使用Negroni这个包,使创建中间件变得更简单。当然你也可以自己随意的实现。Negroni封装了router,并为中间件的管理提供了功能。比如更简单,灵活地连接中间件。
    实现-对Handler的封装
    为了尽可能的简单,下面实现一个中间件来记录网络请求的执行时间。将使用Negroni来实现,先创建router然后用Negroni来封装:
    package main
    
    import (
        "fmt"
        "net/http"
    
        "github.com/gorilla/mux"
        "github.com/urfave/negroni"
    )
    
    func main() {
        router := mux.NewRouter()
        router.
            Methods("GET").
            Path("/").
            HandlerFunc(endpointHandler)
    
        n := negroni.New()
        n.UseHandler(router)
    
        err := http.ListenAndServe(":8080", n)
        if err != nil {
            panic(err)
        }
    }
    
    func endpointHandler(w http.ResponseWriter, r *http.Request) {
        fmt.Println("Endpoint handler called")
    }
    如上面代码所示,先用gorilla/mux包创建router,然后注册一个Get服务。并且创建一个Negroni实例,将router作为参数传入UserHanler函数。因为Negroni实现了Handler接口,可以像使用router那样来使用它。运行以上代码并发起Get请求(curl localhost:8080/ ),可以观察到“Endpoint handler called”内容返回给客户端。
    中间件的实现
    下面继续创建中间件,如前所述,中间件就是一个Handler。因此,我们需要做的就是创建一个结构体来实现Handler接口。这里我们使用Negroni,我们只需要实现它的Handler接口即可。
    如下所示,和net/http.Hanler接口类似,除了它还支持与下一个参数链接:
    type Handler interface {
        ServeHTTP(
            rw http.ResponseWriter, 
            r *http.Request, 
            next http.HandlerFunc,
    )
    }
    让我们创建一个简单的打印日志程序:
    package mw
    
    import (
        "fmt"
        "net/http"
        "time"
    )
    
    type Logger struct{}
    
    func (*Logger) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
        fmt.Println("The logger middleware is executing!")
        t := time.Now()
        next.ServeHTTP(w, r)
        fmt.Printf("Execution time: %s \n", time.Now().Sub(t).String())
    }
    上面的代码展示了如何通过实现Negroni.Handler来创建一个简单的中间件。对请求的执行时间进行记录。最后,继续执行下一个Handler,它可能是另一个中间件或是最终的处理函数。在该调用完成执行后,记录总的执行时间。
    将上面日志中间件添加到router上,看看执行时间:
    package main
    
    import (
        "fmt"
        "net/http"
    
        "github.com/gorilla/mux"
        "github.com/johan-lejdung/go-microservice-middleware-guide/mw"
        "github.com/urfave/negroni"
    )
    
    func main() {
        router := mux.NewRouter()
        router.
            Methods("GET").
            Path("/").
            HandlerFunc(endpointHandler)
    
        n := negroni.New()
        n.Use(&mw.Logger{})
        n.UseHandler(router)
    
        err := http.ListenAndServe(":8080", n)
        if err != nil {
            panic(err)
        }
    }
    
    func endpointHandler(w http.ResponseWriter, r *http.Request) {
        fmt.Println("Endpoint handler called")
    }
    我们只是使用n.Use(&mw.Logger{}),告诉Negroni调用创建的Logger中间件,中间件会按照添加顺序执行。执行上面的代码就会得到如下日志:
    The logger middleware is executing!
    Endpoint handler called
    Execution time: 25.146µs
    第一行和第三行是从中间件生成的,而第二行是在函数endpoint Handler中生成的。我们成功地实现了中间件。
    让我们考虑一下,我们还有一个身份验证中间件(AuthMW),这个AuthMW将确保未经授权的请求无法到达最终执行函数。要将它添加到链中,我们只需执行以下操作:
    n.Use(&mw.Logger{})
    n.Use(&mw.Auth{})
  • 相关阅读:
    Anaconda + Djongo + spyder 网站开发 (三)
    Anaconda + Djongo + spyder 网站开发 (二)
    Anaconda + Djongo + spyder 网站开发 (一)
    实验室网盘链接方式
    R 缓存画图代码,之后再总结
    换源的重要性!!!!
    latex 调整页边距
    Latex 字体设置
    嵌套交叉验证
    FDR及Benjamini-Hochberg方法
  • 原文地址:https://www.cnblogs.com/taotaozhuanyong/p/15825727.html
Copyright © 2020-2023  润新知