英文:
Is it possible to recover from panic on google app engine?
问题
我想知道是否有可能从恐慌中恢复。GAE似乎有自己的恐慌恢复机制,但我找不到任何处理它的钩子来在我的应用程序中处理。
英文:
I'm wondering if it's possible to recover from a panic. It seems GAE has it's own panic recovery mechanism but I can't find any hook to handle it on my app.
答案1
得分: 1
在AppEngine webapp中,处理程序的注册方式与普通的Go应用程序相同。你只需要不显式调用http.ListenAndServe()
(因为平台会处理),而是在init()
函数中进行处理程序的注册(而不是在main()
函数中)。
话虽如此,在AppEngine中也可以使用相同的panic-recover包装方法,不过很遗憾,没有其他更好的方法。
看一下这个示例:它使用一个通过HandleFunc()
注册的函数和一个通过Handle()
注册的Handler
来处理两个URL模式,但它们都故意引发panic(拒绝提供服务):
func myHandleFunc(w http.ResponseWriter, r *http.Request) {
panic("I'm myHandlerFunc and I refuse to serve!")
}
type MyHandler int
func (m *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
panic("I'm MyHandler and I refuse to serve!")
}
func main() {
http.HandleFunc("/myfunc", myHandleFunc)
http.Handle("/myhandler", new(MyHandler))
panic(http.ListenAndServe(":8080", nil))
}
将浏览器定向到http://localhost:8080/myfunc
和http://localhost:8080/myhandler
会导致HTTP 500状态:内部服务器错误(或者取决于你在哪里检查它的空响应)。
总体思路是使用recover
来“捕获”处理程序引发的panic(规范:处理panic)。我们可以以一种方式“包装”处理函数或处理程序,首先注册一个defer
语句,即使函数的其余部分引发panic,也会调用该语句,并在其中从panic状态中恢复。
看一下这两个函数:
func protectFunc(hf func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
defer func() {
r := recover()
if r != nil {
// hf()引发了panic,我们刚刚从中恢复过来。
// 以某种方式处理错误,提供自定义错误页面。
w.Write([]byte("Something went bad but I recovered and sent this!"))
}
}()
hf(w, r)
}
}
func protectHandler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
r := recover()
if r != nil {
// h.ServeHTTP()引发了panic,我们刚刚从中恢复过来。
// 以某种方式处理错误,提供自定义错误页面。
w.Write([]byte("Something went bad but I recovered and sent this!"))
}
}()
h.ServeHTTP(w, r)
})
}
第一个函数接受一个函数,并返回一个调用我们传递的函数但在引发panic时从panic状态中恢复的函数。
第二个函数接受一个Handler
并返回另一个Handler
,类似地调用传递的Handler
,但也处理panic并恢复正常执行。
现在,如果我们使用这些方法注册受保护的处理函数和Handler
,注册的处理程序将永远不会引发panic(假设在恢复正常执行之后的代码不会引发panic):
http.HandleFunc("/myfunc-protected", protectFunc(myHandleFunc))
http.Handle("/myhandler-protected", protectHandler(new(MyHandler)))
访问http://localhost:8080/myfunc-protected
和http://localhost:8080/myhandler-protected
URL会得到HTTP 200状态(OK),并显示以下消息:
Something went bad but I recovered and sent this!
英文:
Handlers in an AppEngine webapp are registered in the same way as would be in a normal Go application. You just don't have to call http.ListenAndServe()
explicitly (because it will be by the platform), and handler registration happens in an init()
function (not in main()
).
Having said that, the same panic-recover wrapping works on AppEngine too, and unfortunately there is no other, better way.
Take a look at this example: it uses a function registered with HandleFunc()
and a Handler
registered with Handle()
to handle 2 URL patterns, but both intentionally panics (they refuse to serve):
func myHandleFunc(w http.ResponseWriter, r *http.Request) {
panic("I'm myHandlerFunc and I refuse to serve!")
}
type MyHandler int
func (m *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
panic("I'm MyHandler and I refuse to serve!")
}
func main() {
http.HandleFunc("/myfunc", myHandleFunc)
http.Handle("/myhandler", new(MyHandler))
panic(http.ListenAndServe(":8080", nil))
}
Directing your browser to http://localhost:8080/myfunc
and http://localhost:8080/myhandler
results in HTTP 500 status: internal server error (or an empty response depending on where you check it).
The general idea is to use recover
to "catch" the panics from handlers (spec: Handling panics). We can "wrap" handle functions or Handlers in a way that we first register a defer
statement which is called even if the rest of the function panics, and in which we recover from the panicing state.
See these 2 functions:
func protectFunc(hf func(http.ResponseWriter,
*http.Request)) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
defer func() {
r := recover()
if r != nil {
// hf() paniced, we just recovered from it.
// Handle error somehow, serve custom error page.
w.Write([]byte("Something went bad but I recovered and sent this!"))
}
}()
hf(w, r)
}
}
func protectHandler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
r := recover()
if r != nil {
// h.ServeHTTP() paniced, we just recovered from it.
// Handle error somehow, serve custom error page.
w.Write([]byte("Something went bad but I recovered and sent this!"))
}
}()
h.ServeHTTP(w, r)
})
}
The first one takes a function and returns one which calles the one we passed but recovers from panicing state if one was initiated.
The second one takes a Handler
and returns another Handler
which similarly calls the passed one but also handles panics and restores normal execution.
Now if we register handler functions and Handler
s protected by these methods, the registered handlers will never panic (assuming the code after restoring normal execution does not panic):
http.HandleFunc("/myfunc-protected", protectFunc(myHandleFunc))
http.Handle("/myhandler-protected", protectHandler(new(MyHandler)))
Visiting http://localhost:8080/myfunc-protected
and http://localhost:8080/myhandler-protected
URLs resuls in HTTP 200 status (OK) with the message:
Something went bad but I recovered and sent this!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论