如何避免 Google App Engine 标准环境中的 Gorilla 会话内存泄漏?

How to avoid memory leak with Gorilla sessions on Google App Engine standard environment?

我正在使用 Gorilla 在 Google App Engine 上启用会话变量。到目前为止,我只导入了 "github.com/gorilla/sessions",但 Gorilla 的页面显示:

If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory! An easy way to do this is to wrap the top-level mux when calling http.ListenAndServe:

http.ListenAndServe(":8080", context.ClearHandler(http.DefaultServeMux))

我的问题是如何针对 App Engine 标准环境调整它,据我所知默认情况下不使用 http.ListenAndServe。我的代码如下所示:

package test

import (
    "fmt"
    "net/http"
    "github.com/gorilla/sessions"   
)

func init() {
    http.HandleFunc("/", handler)
}

func handler(w http.ResponseWriter, r *http.Request) {

    var cookiestore =  sessions.NewCookieStore([]byte("somesecret"))
    session, _ := cookiestore.Get(r, "session")
    session.Values["foo"] = "bar"

    fmt.Fprintf(w, "session value is %v", session.Values["foo"])

}

这样会导致内存泄漏吗?

Gorilla 还提供了一个 context.Clear() 选项,您可以根据此处的文档将其放入请求处理程序中:

https://godoc.org/github.com/gorilla/context#Clear

因此您的处理程序函数将变为:

func handler(w http.ResponseWriter, r *http.Request) {

    var cookiestore =  sessions.NewCookieStore([]byte("somesecret"))
    session, _ := cookiestore.Get(r, "session")
    session.Values["foo"] = "bar"

    fmt.Fprintf(w, "session value is %v", session.Values["foo"])
    context.Clear(r)
}

您引用的文档告诉您需要做的一切:使用 context.ClearHandler(). Since you "only" have a handler function and not an http.Handler, you may use the http.HandlerFunc 适配器包装您的处理程序以获得实现 http.Handler:

的值
func init() {
    http.Handle("/", context.ClearHandler(http.HandlerFunc(handler)))
}

您需要为注册的每个处理程序执行此操作。这就是为什么文档提到只包装您传递给 http.ListenAndServe() 的根处理程序会更容易(这样您就不必包装其他非顶级处理程序)。但这不是唯一的方法,只是最简单/最短的方法。

如果您不自己调用 http.ListenAndServe()(如在 App Engine 中),或者您没有单个根处理程序,则需要手动包装您注册的所有处理程序。

请注意,context.ClearHandler() 返回的处理程序没有任何神奇之处,它所做的只是在调用您传递的处理程序之后调用 context.Clear()。所以你可以很容易地在你的处理程序中调用 context.Clear() 来达到同样的效果。

如果你这样做,一件重要的事情是使用 defer 就好像由于某种原因 context.Clear() 不会达到(例如遇到前面的 return 语句),你会再次泄漏内存。即使您的函数发生恐慌,也会调用延迟函数。所以应该这样做:

func handler(w http.ResponseWriter, r *http.Request) {
    defer context.Clear(r)

    var cookiestore =  sessions.NewCookieStore([]byte("somesecret"))
    session, _ := cookiestore.Get(r, "session")
    session.Values["foo"] = "bar"

    fmt.Fprintf(w, "session value is %v", session.Values["foo"])
}

另请注意,会话存储的创建只能进行一次,因此请将其从您的处理程序中移出到全局变量中。并检查和处理错误,以免您头疼。所以最后建议的代码是这样的:

package test

import (
    "fmt"
    "net/http"
    "log"

    "github.com/gorilla/context"
    "github.com/gorilla/sessions"
)

func init() {
    http.Handle("/", context.ClearHandler(http.HandlerFunc(handler)))
}

var cookiestore =  sessions.NewCookieStore([]byte("somesecret"))

func handler(w http.ResponseWriter, r *http.Request) {
    session, err := cookiestore.Get(r, "session")
    if err != nil {
        // Handle error:
        log.Printf("Error getting session: %v", err)
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    session.Values["foo"] = "bar"

    fmt.Fprintf(w, "session value is %v", session.Values["foo"])
}