optimize WebServer in hook priority handler and request exit mechanism

This commit is contained in:
John 2019-01-13 00:43:36 +08:00
parent 651bd33b73
commit 018853e976
10 changed files with 101 additions and 39 deletions

View File

@ -44,7 +44,6 @@
1. WebServer增加可选择的路由覆盖配置默认情况下不覆盖
1. gkafka这个包比较重未来从框架中剥离出来
1. grpool性能压测结果变慢的问题
1. glist增加Element类型的并发安全处理
1. 改进证书打开失败时的WebServer错误提示前置HOOK校验后关闭后续的HOOK逻辑执行
1. 目前WebServer的HOOK是按照优先级执行的需要增加覆盖特性
1. 增加jumplist的数据结构容器

View File

@ -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

View File

@ -118,6 +118,7 @@ const (
gROUTE_REGISTER_OBJECT = 2
gROUTE_REGISTER_CONTROLLER = 3
gEXCEPTION_EXIT = "exit"
gEXCEPTION_EXIT_HOOK = "exit_hook"
)
var (

View File

@ -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,

View File

@ -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
}

View File

@ -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 {

View File

@ -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)

View File

@ -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)

View 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()
}

View File

@ -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)
}
}