From 4e5877923dda092d53a6fe11e3328630a4b8c007 Mon Sep 17 00:00:00 2001 From: John Date: Mon, 19 Nov 2018 21:49:43 +0800 Subject: [PATCH] dev --- g/net/ghttp/ghttp_server.go | 172 +++++++++--------- g/net/ghttp/ghttp_server_router.go | 8 +- .../server/hooks/same_route_multi_hook.go | 25 +++ 3 files changed, 117 insertions(+), 88 deletions(-) create mode 100644 geg/net/ghttp/server/hooks/same_route_multi_hook.go diff --git a/g/net/ghttp/ghttp_server.go b/g/net/ghttp/ghttp_server.go index 223ee70fd..164ad2288 100644 --- a/g/net/ghttp/ghttp_server.go +++ b/g/net/ghttp/ghttp_server.go @@ -33,6 +33,75 @@ import ( "time" ) +type ( + // Server结构体 + Server struct { + // 基本属性变量 + name string // 服务名称,方便识别 + paths *gspath.SPath // 静态文件检索对象(类似nginx tryfile功能) + config ServerConfig // 配置对象 + servers []*gracefulServer // 底层http.Server列表 + methodsMap map[string]struct{} // 所有支持的HTTP Method(初始化时自动填充) + servedCount *gtype.Int // 已经服务的请求数(4-8字节,不考虑溢出情况),同时作为请求ID + // 服务注册相关 + serveTree map[string]interface{} // 所有注册的服务回调函数(路由表,树型结构,哈希表+链表优先级匹配) + hooksTree map[string]interface{} // 所有注册的事件回调函数(路由表,树型结构,哈希表+链表优先级匹配) + serveCache *gmap.StringInterfaceMap // 服务注册路由内存缓存 + hooksCache *gmap.StringInterfaceMap // 事件回调路由内存缓存 + routesMap map[string]registeredRouteItem // 已经注册的路由及对应的注册方法文件地址(用以路由重复注册判断) + // 自定义状态码回调 + hsmu sync.RWMutex // status handler互斥锁 + statusHandlerMap map[string]HandlerFunc // 不同状态码下的注册处理方法(例如404状态时的处理方法) + // SESSION + sessions *gcache.Cache // Session内存缓存 + // Logger + logger *glog.Logger // 日志管理对象 + } + + // 路由对象 + Router struct { + Uri string // 注册时的pattern - uri + Method string // 注册时的pattern - method + Domain string // 注册时的pattern - domain + RegRule string // 路由规则解析后对应的正则表达式 + RegNames []string // 路由规则解析后对应的变量名称数组 + Priority int // 优先级,用于链表排序,值越大优先级越高 + } + + // http回调函数注册信息 + handlerItem struct { + name string // 注册的方法名称信息 + rtype int // 注册方式(执行对象/回调函数/控制器) + ctype reflect.Type // 控制器类型(反射类型) + fname string // 回调方法名称 + faddr HandlerFunc // 准确的执行方法内存地址(与以上两个参数二选一) + finit HandlerFunc // 初始化请求回调方法(执行对象注册方式下有效) + fshut HandlerFunc // 完成请求回调方法(执行对象注册方式下有效) + router *Router // 注册时绑定的路由对象 + } + + // 根据特定URL.Path解析后的路由检索结果项 + handlerParsedItem struct { + handler *handlerItem // 路由注册项 + values map[string][]string // 特定URL.Path的Router解析参数 + } + + // 已注册的路由项 + registeredRouteItem struct { + file string // 文件路径及行数地址 + handler *handlerItem // 路由注册项 + } + + // pattern与回调函数的绑定map + handlerMap map[string]*handlerItem + + // HTTP注册函数 + HandlerFunc func(r *Request) + + // 文件描述符map + listenerFdMap map[string]string +) + const ( SERVER_STATUS_STOPPED = 0 // Server状态:停止 SERVER_STATUS_RUNNING = 1 // Server状态:运行 @@ -42,8 +111,7 @@ const ( HOOK_AFTER_OUTPUT = "AfterOutput" HOOK_BEFORE_CLOSE = "BeforeClose" HOOK_AFTER_CLOSE = "AfterClose" -) -const ( + gHTTP_METHODS = "GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE" gDEFAULT_SERVER = "default" gDEFAULT_DOMAIN = "default" @@ -54,93 +122,27 @@ const ( gEXCEPTION_EXIT = "exit" ) -// ghttp.Server结构体 -type Server struct { - // 基本属性变量 - name string // 服务名称,方便识别 - paths *gspath.SPath // 静态文件检索对象(类似nginx tryfile功能) - config ServerConfig // 配置对象 - servers []*gracefulServer // 底层http.Server列表 - methodsMap map[string]struct{} // 所有支持的HTTP Method(初始化时自动填充) - servedCount *gtype.Int // 已经服务的请求数(4-8字节,不考虑溢出情况),同时作为请求ID - // 服务注册相关 - serveTree map[string]interface{} // 所有注册的服务回调函数(路由表,树型结构,哈希表+链表优先级匹配) - hooksTree map[string]interface{} // 所有注册的事件回调函数(路由表,树型结构,哈希表+链表优先级匹配) - serveCache *gmap.StringInterfaceMap // 服务注册路由内存缓存 - hooksCache *gmap.StringInterfaceMap // 事件回调路由内存缓存 - routesMap map[string]registeredRouteItem // 已经注册的路由及对应的注册方法文件地址(用以路由重复注册判断) - // 自定义状态码回调 - hsmu sync.RWMutex // status handler互斥锁 - statusHandlerMap map[string]HandlerFunc // 不同状态码下的注册处理方法(例如404状态时的处理方法) - // SESSION - sessions *gcache.Cache // Session内存缓存 - // Logger - logger *glog.Logger // 日志管理对象 -} +var ( + // Server表,用以存储和检索名称与Server对象之间的关联关系 + serverMapping = gmap.NewStringInterfaceMap() -// 路由对象 -type Router struct { - Uri string // 注册时的pattern - uri - Method string // 注册时的pattern - method - Domain string // 注册时的pattern - domain - RegRule string // 路由规则解析后对应的正则表达式 - RegNames []string // 路由规则解析后对应的变量名称数组 - Priority int // 优先级,用于链表排序,值越大优先级越高 -} + // 正常运行的Server数量,如果没有运行、失败或者全部退出,那么该值为0 + serverRunning = gtype.NewInt() -// pattern与回调函数的绑定map -type handlerMap map[string]*handlerItem + // Web Socket默认配置 + wsUpgrader = websocket.Upgrader { + // 默认允许WebSocket请求跨域,权限控制可以由业务层自己负责,灵活度更高 + CheckOrigin: func(r *http.Request) bool { + return true + }, + } + // Web Server已完成服务事件通道,当有事件时表示服务完成,当前进程退出 + doneChan = make(chan struct{}, 1000) -// http回调函数注册信息 -type handlerItem struct { - name string // 注册的方法名称信息 - rtype int // 注册方式(执行对象/回调函数/控制器) - ctype reflect.Type // 控制器类型(反射类型) - fname string // 回调方法名称 - faddr HandlerFunc // 准确的执行方法内存地址(与以上两个参数二选一) - finit HandlerFunc // 初始化请求回调方法(执行对象注册方式下有效) - fshut HandlerFunc // 完成请求回调方法(执行对象注册方式下有效) - router *Router // 注册时绑定的路由对象 -} + // 用于服务进程初始化,只能初始化一次,采用“懒初始化”(在server运行时才初始化) + serverProcInited = gtype.NewBool() +) -// 根据特定URL.Path解析后的路由检索结果项 -type handlerParsedItem struct { - handler *handlerItem // 路由注册项 - values map[string][]string // 特定URL.Path的Router解析参数 -} - -// 已注册的路由项 -type registeredRouteItem struct { - file string // 文件路径及行数地址 - handler *handlerItem // 路由注册项 -} - -// HTTP注册函数 -type HandlerFunc func(r *Request) - -// 文件描述符map -type listenerFdMap map[string]string - - -// Server表,用以存储和检索名称与Server对象之间的关联关系 -var serverMapping = gmap.NewStringInterfaceMap() - -// 正常运行的Server数量,如果没有运行、失败或者全部退出,那么该值为0 -var serverRunning = gtype.NewInt() - -// Web Socket默认配置 -var wsUpgrader = websocket.Upgrader { - // 默认允许WebSocket请求跨域,权限控制可以由业务层自己负责,灵活度更高 - CheckOrigin: func(r *http.Request) bool { - return true - }, -} - -// Web Server已完成服务事件通道,当有事件时表示服务完成,当前进程退出 -var doneChan = make(chan struct{}, 1000) - -// 用于服务进程初始化,只能初始化一次,采用“懒初始化”(在server运行时才初始化) -var serverProcInited = gtype.NewBool() // Web Server进程初始化. // 注意该方法不能放置于包初始化方法init中,不使用ghttp.Server的功能便不能初始化对应的协程goroutine逻辑. diff --git a/g/net/ghttp/ghttp_server_router.go b/g/net/ghttp/ghttp_server_router.go index 8087479d7..aab81f21f 100644 --- a/g/net/ghttp/ghttp_server_router.go +++ b/g/net/ghttp/ghttp_server_router.go @@ -55,7 +55,7 @@ func (s *Server) getHandlerRegisterCallerLine(handler *handlerItem) string { } // 路由注册处理方法。 -// 如果带有hook参数,表示是回调注册方法,否则为普通路由执行方法。 +// 如果带有hook参数,表示是回调注册方法; 否则为普通路由执行方法。 func (s *Server) setHandler(pattern string, handler *handlerItem, hook ... string) (resultErr error) { // Web Server正字运行时无法动态注册路由方法 if s.Status() == SERVER_STATUS_RUNNING { @@ -69,6 +69,7 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ... strin if err != nil { return errors.New("invalid pattern") } + // 注册地址记录及重复注册判断 regkey := s.hookHandlerKey(hookName, method, uri, domain) caller := s.getHandlerRegisterCallerLine(handler) if item, ok := s.routesMap[regkey]; ok { @@ -156,8 +157,8 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ... strin } } } - // 得到的lists是该路由规则一路匹配下来相关的模糊匹配链表(注意不是这棵树所有的链表), - // 从头开始遍历每个节点的模糊匹配链表,将该路由项插入进去(按照优先级高的放在前面) + // 上面循环后得到的lists是该路由规则一路匹配下来相关的模糊匹配链表(注意不是这棵树所有的链表)。 + // 下面从头开始遍历每个节点的模糊匹配链表,将该路由项插入进去(按照优先级高的放在lists链表的前面) item := (*handlerItem)(nil) for _, l := range lists { pushed := false @@ -173,6 +174,7 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ... strin break } } + // 如果路由注册项不相等,那么判断优先级,决定插入顺序 if s.compareRouterPriority(handler.router, item.router) { l.InsertBefore(handler, e) pushed = true diff --git a/geg/net/ghttp/server/hooks/same_route_multi_hook.go b/geg/net/ghttp/server/hooks/same_route_multi_hook.go new file mode 100644 index 000000000..8ceec2771 --- /dev/null +++ b/geg/net/ghttp/server/hooks/same_route_multi_hook.go @@ -0,0 +1,25 @@ +package main + +import ( + "gitee.com/johng/gf/g" + "gitee.com/johng/gf/g/net/ghttp" +) + +// 允许对同一个路由同一个事件注册多个回调函数,按照注册顺序进行优先级调用 +func main() { + s := g.Server() + s.BindHandler("/", func(r *ghttp.Request) { + r.Response.Writeln(r.GetParam("name").String()) + r.Response.Writeln(r.GetParam("site").String()) + }) + s.BindHookHandler("/", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) { + r.SetParam("name", "GoFrame") + r.Response.Writeln("set name") + }) + s.BindHookHandler("/", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) { + r.SetParam("site", "https://gfer.me") + r.Response.Writeln("set site") + }) + s.SetPort(8199) + s.Run() +} \ No newline at end of file