mirror of
https://gitee.com/johng/gf.git
synced 2024-12-01 11:48:09 +08:00
optimize WebServer in hook priority handler and request exit mechanism
This commit is contained in:
parent
651bd33b73
commit
018853e976
1
TODO.MD
1
TODO.MD
@ -44,7 +44,6 @@
|
||||
1. WebServer增加可选择的路由覆盖配置,默认情况下不覆盖;
|
||||
1. gkafka这个包比较重,未来从框架中剥离出来;
|
||||
1. grpool性能压测结果变慢的问题;
|
||||
1. glist增加Element类型的并发安全处理;
|
||||
1. 改进证书打开失败时的WebServer错误提示,前置HOOK校验后关闭后续的HOOK逻辑执行;
|
||||
1. 目前WebServer的HOOK是按照优先级执行的,需要增加覆盖特性;
|
||||
1. 增加jumplist的数据结构容器;
|
||||
|
@ -147,12 +147,22 @@ func (r *Request) GetToStruct(object interface{}, mapping...map[string]string) {
|
||||
r.GetRequestToStruct(object, mapping...)
|
||||
}
|
||||
|
||||
// 退出当前请求执行,原理是在Request.exit做标记,由服务逻辑流程做判断,自行停止
|
||||
// 仅退出当前逻辑执行函数, 如:服务函数、HOOK函数
|
||||
func (r *Request) Exit() {
|
||||
panic(gEXCEPTION_EXIT)
|
||||
}
|
||||
|
||||
// 退出当前请求执行,后续所有的服务逻辑流程(包括其他的HOOK)将不会执行
|
||||
func (r *Request) ExitAll() {
|
||||
r.exit = true
|
||||
panic(gEXCEPTION_EXIT)
|
||||
}
|
||||
|
||||
// 仅针对HOOK执行,默认情况下HOOK会按照优先级进行调用,当使用ExitHook后当前类型的后续HOOK将不会被调用
|
||||
func (r *Request) ExitHook() {
|
||||
panic(gEXCEPTION_EXIT_HOOK)
|
||||
}
|
||||
|
||||
// 判断当前请求是否停止执行
|
||||
func (r *Request) IsExited() bool {
|
||||
return r.exit
|
||||
|
@ -118,6 +118,7 @@ const (
|
||||
gROUTE_REGISTER_OBJECT = 2
|
||||
gROUTE_REGISTER_CONTROLLER = 3
|
||||
gEXCEPTION_EXIT = "exit"
|
||||
gEXCEPTION_EXIT_HOOK = "exit_hook"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -52,7 +52,7 @@ type ServerConfig struct {
|
||||
ServerRoot string // 服务器服务的本地目录根路径(检索优先级比StaticPaths低)
|
||||
SearchPaths []string // 静态文件搜索目录(包含ServerRoot,按照优先级进行排序)
|
||||
StaticPaths []staticPathItem // 静态文件目录映射(按照优先级进行排序)
|
||||
FileServerEnabled bool // 是否允许静态文件服务(总开关,默认开启)
|
||||
FileServerEnabled bool // 是否允许静态文件服务(通过静态文件服务方法调用自动识别)
|
||||
|
||||
// COOKIE
|
||||
CookieMaxAge int // Cookie有效期
|
||||
@ -99,7 +99,7 @@ var defaultServerConfig = ServerConfig {
|
||||
ServerAgent : "gf",
|
||||
ServerRoot : "",
|
||||
StaticPaths : make([]staticPathItem, 0),
|
||||
FileServerEnabled : true,
|
||||
FileServerEnabled : false,
|
||||
|
||||
CookieMaxAge : gDEFAULT_COOKIE_MAX_AGE,
|
||||
CookiePath : gDEFAULT_COOKIE_PATH,
|
||||
|
@ -64,7 +64,8 @@ func (s *Server)SetServerRoot(root string) {
|
||||
if path == "" {
|
||||
glog.Fatal(fmt.Sprintf(`[ghttp] SetServerRoot failed: path "%s" does not exist`, root))
|
||||
}
|
||||
s.config.SearchPaths = []string{strings.TrimRight(path, gfile.Separator)}
|
||||
s.config.SearchPaths = []string{strings.TrimRight(path, gfile.Separator)}
|
||||
s.config.FileServerEnabled = true
|
||||
}
|
||||
|
||||
// 添加静态文件搜索**目录**,必须给定目录的绝对路径
|
||||
@ -81,7 +82,8 @@ func (s *Server) AddSearchPath(path string) {
|
||||
if realPath == "" {
|
||||
glog.Fatal(fmt.Sprintf(`[ghttp] AddSearchPath failed: path "%s" does not exist`, path))
|
||||
}
|
||||
s.config.SearchPaths = append(s.config.SearchPaths, realPath)
|
||||
s.config.SearchPaths = append(s.config.SearchPaths, realPath)
|
||||
s.config.FileServerEnabled = true
|
||||
}
|
||||
|
||||
// 添加URI与静态**目录**的映射
|
||||
@ -132,5 +134,6 @@ func (s *Server) AddStaticPath(prefix string, path string) {
|
||||
} else {
|
||||
s.config.StaticPaths = []staticPathItem { addItem }
|
||||
}
|
||||
s.config.FileServerEnabled = true
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"fmt"
|
||||
"net"
|
||||
@ -92,11 +93,11 @@ func (s *gracefulServer) ListenAndServeTLS(certFile, keyFile string) error {
|
||||
if config.NextProtos == nil {
|
||||
config.NextProtos = []string{"http/1.1"}
|
||||
}
|
||||
var err error
|
||||
err := error(nil)
|
||||
config.Certificates = make([]tls.Certificate, 1)
|
||||
config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.New(fmt.Sprintf(`open cert file "%s","%s" failed: %s`, certFile, keyFile, err.Error()))
|
||||
}
|
||||
ln, err := s.getNetListener(addr)
|
||||
if err != nil {
|
||||
|
@ -118,19 +118,25 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// 事件 - AfterServe
|
||||
s.callHookHandler(HOOK_AFTER_SERVE, request)
|
||||
if !request.IsExited() {
|
||||
s.callHookHandler(HOOK_AFTER_SERVE, request)
|
||||
}
|
||||
|
||||
// 设置请求完成时间
|
||||
request.LeaveTime = gtime.Microsecond()
|
||||
|
||||
// 事件 - BeforeOutput
|
||||
s.callHookHandler(HOOK_BEFORE_OUTPUT, request)
|
||||
if !request.IsExited() {
|
||||
s.callHookHandler(HOOK_BEFORE_OUTPUT, request)
|
||||
}
|
||||
// 输出Cookie
|
||||
request.Cookie.Output()
|
||||
// 输出缓冲区
|
||||
request.Response.OutputBuffer()
|
||||
// 事件 - AfterOutput
|
||||
s.callHookHandler(HOOK_AFTER_OUTPUT, request)
|
||||
if !request.IsExited() {
|
||||
s.callHookHandler(HOOK_AFTER_OUTPUT, request)
|
||||
}
|
||||
}
|
||||
|
||||
// 查找静态文件的绝对路径
|
||||
@ -158,38 +164,32 @@ func (s *Server) searchStaticFile(uri string) (filePath string, isDir bool) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
// 初始化控制器
|
||||
// 调用服务接口
|
||||
func (s *Server) callServeHandler(h *handlerItem, r *Request) {
|
||||
if h.faddr == nil {
|
||||
c := reflect.New(h.ctype)
|
||||
s.niceCall(func() {
|
||||
s.niceCallFunc(func() {
|
||||
c.MethodByName("Init").Call([]reflect.Value{reflect.ValueOf(r)})
|
||||
})
|
||||
s.niceCall(func() {
|
||||
s.niceCallFunc(func() {
|
||||
c.MethodByName(h.fname).Call(nil)
|
||||
})
|
||||
s.niceCall(func() {
|
||||
s.niceCallFunc(func() {
|
||||
c.MethodByName("Shut").Call(nil)
|
||||
})
|
||||
} else {
|
||||
if h.finit != nil {
|
||||
s.niceCall(func() {
|
||||
h.finit(r)
|
||||
})
|
||||
s.niceCallServeHandler(h.finit, r)
|
||||
}
|
||||
s.niceCall(func() {
|
||||
h.faddr(r)
|
||||
})
|
||||
s.niceCallServeHandler(h.faddr, r)
|
||||
if h.fshut != nil {
|
||||
s.niceCall(func() {
|
||||
h.fshut(r)
|
||||
})
|
||||
s.niceCallServeHandler(h.fshut, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 友好地调用方法
|
||||
func (s *Server) niceCall(f func()) {
|
||||
// 友好地调用通用方法
|
||||
func (s *Server) niceCallFunc(f func()) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil && e != gEXCEPTION_EXIT {
|
||||
panic(e)
|
||||
@ -198,6 +198,16 @@ func (s *Server) niceCall(f func()) {
|
||||
f()
|
||||
}
|
||||
|
||||
// 友好地调用HandlerFunc
|
||||
func (s *Server) niceCallServeHandler(f HandlerFunc, r *Request) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil && e != gEXCEPTION_EXIT {
|
||||
panic(e)
|
||||
}
|
||||
}()
|
||||
f(r)
|
||||
}
|
||||
|
||||
// http server静态文件处理,path可以为相对路径也可以为绝对路径
|
||||
func (s *Server) serveFile(r *Request, path string) {
|
||||
f, err := os.Open(path)
|
||||
|
@ -46,11 +46,6 @@ func (s *Server) callHookHandler(hook string, r *Request) {
|
||||
}
|
||||
hookItems := s.getHookHandlerWithCache(hook, r)
|
||||
if len(hookItems) > 0 {
|
||||
defer func() {
|
||||
if e := recover(); e != nil && e != gEXCEPTION_EXIT {
|
||||
panic(e)
|
||||
}
|
||||
}()
|
||||
// 备份原有的router变量
|
||||
oldRouterVars := r.routerVars
|
||||
for _, item := range hookItems {
|
||||
@ -70,13 +65,30 @@ func (s *Server) callHookHandler(hook string, r *Request) {
|
||||
}
|
||||
// 不使用hook的router对象,保留路由注册服务的router对象,不能覆盖
|
||||
// r.Router = item.handler.router
|
||||
item.handler.faddr(r)
|
||||
if err := s.niceCallHookHandler(item.handler.faddr, r); err != nil {
|
||||
switch err {
|
||||
case gEXCEPTION_EXIT:
|
||||
case gEXCEPTION_EXIT_HOOK:
|
||||
return
|
||||
default:
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 恢复原有的router变量
|
||||
r.routerVars = oldRouterVars
|
||||
}
|
||||
}
|
||||
|
||||
// 友好地调用方法
|
||||
func (s *Server) niceCallHookHandler(f HandlerFunc, r *Request) (err interface{}) {
|
||||
defer func() {
|
||||
err = recover()
|
||||
}()
|
||||
f(r)
|
||||
return
|
||||
}
|
||||
|
||||
// 查询请求处理方法, 带缓存机制,按照Host、Method、Path进行缓存.
|
||||
func (s *Server) getHookHandlerWithCache(hook string, r *Request) []*handlerParsedItem {
|
||||
cacheItems := ([]*handlerParsedItem)(nil)
|
||||
|
19
geg/net/ghttp/server/hooks/hooks5.go
Normal file
19
geg/net/ghttp/server/hooks/hooks5.go
Normal file
@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g"
|
||||
"gitee.com/johng/gf/g/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHookHandler("/*any", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
r.Response.Writeln("/*any")
|
||||
})
|
||||
s.BindHookHandler("/v1/*", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
r.Response.Writeln("/v1/*")
|
||||
r.ExitHook()
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
@ -1,12 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
import "fmt"
|
||||
|
||||
func test() (err interface{}) {
|
||||
defer func() {
|
||||
err = recover()
|
||||
}()
|
||||
panic(1)
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
i := int64(5)
|
||||
n := int64(3)
|
||||
m := i/n
|
||||
fmt.Println(m)
|
||||
|
||||
switch err := test(); err {
|
||||
default:
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user