From 0be2ddbbcb2e858bcccb3040095090d6658a6145 Mon Sep 17 00:00:00 2001 From: john Date: Fri, 27 Jul 2018 19:03:32 +0800 Subject: [PATCH] =?UTF-8?q?ghttp=E8=B7=AF=E7=94=B1=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E6=94=B9=E8=BF=9B=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- g/net/ghttp/ghttp_server.go | 8 +- g/net/ghttp/ghttp_server_hooks.go | 128 +-------------------- g/net/ghttp/ghttp_server_router.go | 177 +++++++++++++++++++++++------ geg/other/test.go | 1 + 4 files changed, 145 insertions(+), 169 deletions(-) diff --git a/g/net/ghttp/ghttp_server.go b/g/net/ghttp/ghttp_server.go index 0293e743c..0da6d9291 100644 --- a/g/net/ghttp/ghttp_server.go +++ b/g/net/ghttp/ghttp_server.go @@ -57,11 +57,6 @@ type Server struct { handlerMap HandlerMap // 所有注册的回调函数(静态匹配) handlerTree map[string]interface{} // 所有注册的回调函数(动态匹配,树型+链表优先级匹配) handlerCache *gcache.Cache // 服务注册路由内存缓存 - // 事件回调注册 - hhmu sync.RWMutex // hooks互斥锁 - hhcmu sync.RWMutex // hooksCache互斥锁 - hooksTree map[string]interface{} // 所有注册的事件回调函数(动态匹配,树型+链表优先级匹配) - hooksCache *gcache.Cache // 回调事件注册路由内存缓存 // 自定义状态码回调 hsmu sync.RWMutex // status handler互斥锁 statusHandlerMap map[string]HandlerFunc // 不同状态码下的注册处理方法(例如404状态时的处理方法) @@ -82,7 +77,7 @@ type Server struct { } // 域名、URI与回调函数的绑定记录表 -type HandlerMap map[string]*HandlerItem +type HandlerMap map[string]*handlerRegisterItem // 路由对象 type Router struct { @@ -99,7 +94,6 @@ type HandlerItem struct { ctype reflect.Type // 控制器类型 fname string // 回调方法名称 faddr HandlerFunc // 准确的执行方法内存地址(与以上两个参数二选一) - router *Router // 注册时绑定的路由对象 } // HTTP注册函数 diff --git a/g/net/ghttp/ghttp_server_hooks.go b/g/net/ghttp/ghttp_server_hooks.go index 42eeb9501..3f88e6fed 100644 --- a/g/net/ghttp/ghttp_server_hooks.go +++ b/g/net/ghttp/ghttp_server_hooks.go @@ -22,63 +22,8 @@ type hookCacheItem struct { // 事件回调注册方法 // 因为有事件回调优先级的关系,叶子节点必须为一个链表,因此这里只有动态注册 -func (s *Server) setHookHandler(pattern string, hook string, item *HandlerItem) error { - domain, method, uri, err := s.parsePattern(pattern) - if err != nil { - return errors.New("invalid pattern") - } - item.router = &Router { - Uri : uri, - Domain : domain, - Method : method, - } - item.router.RegRule, item.router.RegNames = s.patternToRegRule(uri) - - s.hhmu.Lock() - defer s.hhmu.Unlock() - defer s.clearHooksCache() - - if _, ok := s.hooksTree[domain]; !ok { - s.hooksTree[domain] = make(map[string]interface{}) - } - p := s.hooksTree[domain] - if _, ok := p.(map[string]interface{})[hook]; !ok { - p.(map[string]interface{})[hook] = make(map[string]interface{}) - } - p = p.(map[string]interface{})[hook] - - array := strings.Split(uri[1:], "/") - item.router.Priority = len(array) - for _, v := range array { - if len(v) == 0 { - continue - } - if gregex.IsMatchString(`^[:\*]|{[\w\.\-]+}`, v) { - v = "*fuzz" - } - if _, ok := p.(map[string]interface{})[v]; !ok { - p.(map[string]interface{})[v] = make(map[string]interface{}) - } - p = p.(map[string]interface{})[v] - } - // 到达叶子节点 - var l *list.List - if v, ok := p.(map[string]interface{})["*list"]; !ok { - l = list.New() - p.(map[string]interface{})["*list"] = l - } else { - l = v.(*list.List) - } - // 从头开始遍历链表,优先级高的放在前面 - for e := l.Front(); e != nil; e = e.Next() { - if s.compareHandlerItemPriority(item, e.Value.(*HandlerItem)) { - l.InsertBefore(item, e) - break - } - } - l.PushBack(item) - - return nil +func (s *Server) setHookHandler(pattern string, hook string, handler *HandlerItem) error { + return s.setHandler(pattern, handler, hook) } // 事件回调 - 检索动态路由规则 @@ -113,76 +58,7 @@ func (s *Server) callHookHandler(r *Request, hook string) { } func (s *Server) searchHookHandler(r *Request, hook string) []*hookCacheItem { - s.hhmu.RLock() - defer s.hhmu.RUnlock() - hookItems := make([]*hookCacheItem, 0) - domains := []string{r.GetHost(), gDEFAULT_DOMAIN} - array := strings.Split(r.URL.Path[1:], "/") - for _, domain := range domains { - p, ok := s.hooksTree[domain] - if !ok { - continue - } - p, ok = p.(map[string]interface{})[hook] - if !ok { - continue - } - // 多层链表的目的是当叶子节点未有任何规则匹配时,让父级模糊匹配规则继续处理 - lists := make([]*list.List, 0) - for k, v := range array { - if _, ok := p.(map[string]interface{})["*list"]; ok { - lists = append(lists, p.(map[string]interface{})["*list"].(*list.List)) - } - if _, ok := p.(map[string]interface{})[v]; !ok { - if _, ok := p.(map[string]interface{})["/"]; ok { - p = p.(map[string]interface{})["/"] - if k == len(array) - 1 { - if _, ok := p.(map[string]interface{})["*list"]; ok { - lists = append(lists, p.(map[string]interface{})["*list"].(*list.List)) - } - } - } else { - break - } - } else { - p = p.(map[string]interface{})[v] - if k == len(array) - 1 { - if _, ok := p.(map[string]interface{})["*list"]; ok { - lists = append(lists, p.(map[string]interface{})["*list"].(*list.List)) - } - } - } - } - // 多层链表遍历检索,从数组末尾的链表开始遍历,末尾的深度高优先级也高 - for i := len(lists) - 1; i >= 0; i-- { - for e := lists[i].Front(); e != nil; e = e.Next() { - item := e.Value.(*HandlerItem) - if strings.EqualFold(item.router.Method, gDEFAULT_METHOD) || strings.EqualFold(item.router.Method, r.Method) { - regrule, names := s.patternToRegRule(item.router.Uri) - if gregex.IsMatchString(regrule, r.URL.Path) { - hookItem := &hookCacheItem {item.faddr, nil} - // 如果需要query匹配,那么需要重新解析URL - if len(names) > 0 { - if match, err := gregex.MatchString(regrule, r.URL.Path); err == nil { - array := strings.Split(names, ",") - if len(match) > len(array) { - hookItem.values = make(map[string][]string) - // 这里需要注意的是,注册事件回调如果带有规则匹配,那么会修改Request对象传递参数的值 - // 这个应当在注册事件回调的时候注意 - for index, name := range array { - hookItem.values[name] = []string{match[index + 1]} - } - } - } - } - hookItems = append(hookItems, hookItem) - } - } - } - } - } - return hookItems } // 绑定指定的hook回调函数, pattern参数同BindHandler,支持命名路由;hook参数的值由ghttp server设定,参数不区分大小写 diff --git a/g/net/ghttp/ghttp_server_router.go b/g/net/ghttp/ghttp_server_router.go index 6026d2750..d896a8763 100644 --- a/g/net/ghttp/ghttp_server_router.go +++ b/g/net/ghttp/ghttp_server_router.go @@ -12,12 +12,20 @@ import ( "strings" "container/list" "gitee.com/johng/gf/g/util/gregex" + "gitee.com/johng/gf/g/container/glist" ) +// 路由注册项 +type handlerRegisterItem struct { + handler *HandlerItem // 准确的执行方法内存地址 + hooks map[string]*glist.List // 当前的事件回调注册,键名为事件名称,键值为事件执行方法链表 + router *Router // 注册时绑定的路由对象 +} + // 路由检索缓存项,根据URL.Path进行缓存,因此对象中带有缓存参数 -type routerCacheItem struct { - item *HandlerItem // 准确的执行方法内存地址 - values map[string][]string // GET解析参数 +type handlerCacheItem struct { + item *handlerRegisterItem // 路由注册项 + values map[string][]string // 特定URL.Path的GET解析参数 } // 查询请求处理方法 @@ -27,7 +35,7 @@ func (s *Server) getHandler(r *Request) *HandlerItem { s.hmcmu.RLock() defer s.hmcmu.RUnlock() - var cacheItem *routerCacheItem + var cacheItem *handlerCacheItem cacheKey := s.handlerKey(r.GetHost(), r.Method, r.URL.Path) if v := s.handlerCache.Get(cacheKey); v == nil { cacheItem = s.searchHandler(r) @@ -35,14 +43,15 @@ func (s *Server) getHandler(r *Request) *HandlerItem { s.handlerCache.Set(cacheKey, cacheItem, 0) } } else { - cacheItem = v.(*routerCacheItem) + cacheItem = v.(*handlerCacheItem) } + if cacheItem != nil { for k, v := range cacheItem.values { r.queries[k] = v } r.Router = cacheItem.item.router - return cacheItem.item + return cacheItem.item.handler } return nil } @@ -70,18 +79,39 @@ func (s *Server)parsePattern(pattern string) (domain, method, uri string, err er return } -// 路由注册处理方法 -func (s *Server) setHandler(pattern string, item *HandlerItem) error { +// 路由注册处理方法。 +// 如果带有hook参数,表示是回调注册方法,否则为普通路由执行方法。 +func (s *Server) setHandler(pattern string, handler *HandlerItem, hook ... string) error { + var hookName string + if len(hook) > 0 { + hookName = hook[0] + } domain, method, uri, err := s.parsePattern(pattern) if err != nil { return errors.New("invalid pattern") } - item.router = &Router { - Uri : uri, - Domain : domain, - Method : method, + + // 路由对象 + router := &Router { + Uri : uri, + Domain : domain, + Method : method, + Priority : strings.Count(uri[1:], "/"), } - item.router.RegRule, item.router.RegNames = s.patternToRegRule(uri) + router.RegRule, router.RegNames = s.patternToRegRule(uri) + + // 注册对象 + registerItem := &handlerRegisterItem { + handler : handler, + hooks : make(map[string]*glist.List), + router : router, + } + if len(hookName) > 0 { + registerItem.handler = nil + registerItem.hooks[hookName] = glist.New() + registerItem.hooks[hookName].PushBack(registerItem) + } + s.hmmu.Lock() defer s.hmmu.Unlock() defer s.clearHandlerCache() @@ -97,7 +127,6 @@ func (s *Server) setHandler(pattern string, item *HandlerItem) error { // 当前节点的规则链表 lists := make([]*list.List, 0) array := strings.Split(uri[1:], "/") - item.router.Priority = len(array) // 键名"*fuzz"代表模糊匹配节点,其下会有一个链表; // 键名"*list"代表链表,叶子节点和模糊匹配节点都有该属性; for k, v := range array { @@ -133,24 +162,100 @@ func (s *Server) setHandler(pattern string, item *HandlerItem) error { } // 得到的lists是该路由规则一路匹配下来相关的模糊匹配链表(注意不是这棵树所有的链表), // 从头开始遍历每个节点的模糊匹配链表,将该路由项插入进去(按照优先级高的放在前面) - for _, l := range lists { - for e := l.Front(); e != nil; e = e.Next() { - if s.compareHandlerItemPriority(item, e.Value.(*HandlerItem)) { - l.InsertBefore(item, e) - break + if len(hookName) == 0 { + // 普通方法路由注册 + for _, l := range lists { + pushed := false + for e := l.Front(); e != nil; e = e.Next() { + item := e.Value.(*handlerRegisterItem) + // 判断是否已存在相同的路由注册项 + if strings.EqualFold(router.Domain, item.router.Domain) && + strings.EqualFold(router.Method, item.router.Method) && + strings.EqualFold(router.Uri, item.router.Uri) { + item.handler = handler + pushed = true + break + } + if s.compareRouterPriority(router, item.router) { + l.InsertBefore(registerItem, e) + pushed = true + break + } + } + if !pushed { + l.PushBack(registerItem) + } + } + } else { + // 回调方法路由注册 + for _, l := range lists { + pushed := false + for e := l.Front(); e != nil; e = e.Next() { + item := e.Value.(*handlerRegisterItem) + // 判断是否已存在相同的路由注册项 + if strings.EqualFold(router.Domain, item.router.Domain) && + strings.EqualFold(router.Method, item.router.Method) && + strings.EqualFold(router.Uri, item.router.Uri) { + if _, ok := item.hooks[hookName]; !ok { + item.hooks[hookName] = glist.New() + } + item.hooks[hookName].PushBack(handler) + pushed = true + break + } + if s.compareRouterPriority(router, item.router) { + l.InsertBefore(registerItem, e) + pushed = true + break + } + } + if !pushed { + l.PushBack(registerItem) } } - l.PushBack(item) } } else { // 静态注册 - if method == gDEFAULT_METHOD { - for v, _ := range s.methodsMap { - s.handlerMap[s.handlerKey(domain, v, uri)] = item + if len(hookName) == 0 { + // 普通方法注册 + if method == gDEFAULT_METHOD { + for v, _ := range s.methodsMap { + key := s.handlerKey(domain, v, uri) + if v, ok := s.handlerMap[key]; ok { + v.handler = handler + } else { + s.handlerMap[key] = registerItem + } + } + } else { + key := s.handlerKey(domain, method, uri) + if v, ok := s.handlerMap[key]; ok { + v.handler = handler + } else { + s.handlerMap[key] = registerItem + } } } else { - s.handlerMap[s.handlerKey(domain, method, uri)] = item + // 回调方法注册 + if method == gDEFAULT_METHOD { + for v, _ := range s.methodsMap { + key := s.handlerKey(domain, v, uri) + if v, ok := s.handlerMap[key]; ok { + + } else { + s.handlerMap[key] = registerItem + } + } + } else { + key := s.handlerKey(domain, method, uri) + if v, ok := s.handlerMap[key]; ok { + v.handler = handler + } else { + s.handlerMap[key] = registerItem + } + } } + } //b, _ := gparser.VarToJsonIndent(s.handlerTree) //fmt.Println(string(b)) @@ -161,21 +266,21 @@ func (s *Server) setHandler(pattern string, item *HandlerItem) error { // 优先级比较规则: // 1、层级越深优先级越高(对比/数量); // 2、模糊规则优先级:{xxx} > :xxx > *xxx; -func (s *Server) compareHandlerItemPriority(newItem, oldItem *HandlerItem) bool { - if newItem.router.Priority > oldItem.router.Priority { +func (s *Server) compareRouterPriority(newRouter, oldRouter *Router) bool { + if newRouter.Priority > oldRouter.Priority { return true } - if newItem.router.Priority < oldItem.router.Priority { + if newRouter.Priority < oldRouter.Priority { return false } // 例如:/{user}/{act} 比 /:user/:act 优先级高 - if strings.Count(newItem.router.Uri, "{") > strings.Count(oldItem.router.Uri, "{") { + if strings.Count(newRouter.Uri, "{") > strings.Count(oldRouter.Uri, "{") { return true } // 例如: /:name/update 比 /:name/:action优先级高 - if strings.Count(newItem.router.Uri, "/:") < strings.Count(oldItem.router.Uri, "/:") { + if strings.Count(newRouter.Uri, "/:") < strings.Count(oldRouter.Uri, "/:") { // 例如: /:name/:action 比 /:name/*any 优先级高 - if strings.Count(newItem.router.Uri, "/*") < strings.Count(oldItem.router.Uri, "/*") { + if strings.Count(newRouter.Uri, "/*") < strings.Count(oldRouter.Uri, "/*") { return true } return false @@ -184,7 +289,7 @@ func (s *Server) compareHandlerItemPriority(newItem, oldItem *HandlerItem) bool } // 服务方法检索 -func (s *Server) searchHandler(r *Request) *routerCacheItem { +func (s *Server) searchHandler(r *Request) *handlerCacheItem { item := s.searchHandlerStatic(r) if item == nil { item = s.searchHandlerDynamic(r) @@ -193,21 +298,21 @@ func (s *Server) searchHandler(r *Request) *routerCacheItem { } // 检索静态路由规则 -func (s *Server) searchHandlerStatic(r *Request) *routerCacheItem { +func (s *Server) searchHandlerStatic(r *Request) *handlerCacheItem { s.hmmu.RLock() defer s.hmmu.RUnlock() domains := []string{r.GetHost(), gDEFAULT_DOMAIN} // 首先进行静态匹配 for _, domain := range domains { if f, ok := s.handlerMap[s.handlerKey(domain, r.Method, r.URL.Path)]; ok { - return &routerCacheItem{f, nil} + return &handlerCacheItem{f, nil} } } return nil } // 检索动态路由规则 -func (s *Server) searchHandlerDynamic(r *Request) *routerCacheItem { +func (s *Server) searchHandlerDynamic(r *Request) *handlerCacheItem { s.hmmu.RLock() defer s.hmmu.RUnlock() domains := []string{gDEFAULT_DOMAIN, r.GetHost()} @@ -249,13 +354,13 @@ func (s *Server) searchHandlerDynamic(r *Request) *routerCacheItem { // 多层链表遍历检索,从数组末尾的链表开始遍历,末尾的深度高优先级也高 for i := len(lists) - 1; i >= 0; i-- { for e := lists[i].Front(); e != nil; e = e.Next() { - item := e.Value.(*HandlerItem) + item := e.Value.(*handlerRegisterItem) // 动态匹配规则带有gDEFAULT_METHOD的情况,不会像静态规则那样直接解析为所有的HTTP METHOD存储 if strings.EqualFold(item.router.Method, gDEFAULT_METHOD) || strings.EqualFold(item.router.Method, r.Method) { if match, err := gregex.MatchString(item.router.RegRule, r.URL.Path); err == nil && len(match) > 1 { //gutil.Dump(match) //gutil.Dump(names) - handlerItem := &routerCacheItem{item, nil} + handlerItem := &handlerCacheItem{item, nil} // 如果需要query匹配,那么需要重新正则解析URL if len(item.router.RegNames) > 0 { if len(match) > len(item.router.RegNames) { diff --git a/geg/other/test.go b/geg/other/test.go index b599cbbee..13cb305f4 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -5,6 +5,7 @@ import ( ) func main() { + ghttp. gfile.PutContentsAppend("/tmp/test", "1") gfile.PutContentsAppend("/tmp/test", "2") gfile.PutContentsAppend("/tmp/test", "3")