如何避免 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"])
}
我正在使用 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"])
}