From 9c8d48bf03399ae485e00a1d8946ff2e90a29117 Mon Sep 17 00:00:00 2001 From: John Date: Fri, 13 Apr 2018 18:48:19 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90ghttp.Server=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E6=B3=A8=E5=86=8C=E4=B8=8E=E4=BA=8B=E4=BB=B6=E5=9B=9E?= =?UTF-8?q?=E8=B0=83=E6=B3=A8=E5=86=8C=E7=9A=84=E5=8A=A8=E6=80=81=E8=B7=AF?= =?UTF-8?q?=E7=94=B1=E8=A7=84=E5=88=99=E7=89=B9=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- g/net/ghttp/http_server.go | 22 +-- g/net/ghttp/http_server_handler.go | 79 ----------- g/net/ghttp/http_server_hooks.go | 210 +++++++++++++++++++++++++++++ g/net/ghttp/http_server_router.go | 65 ++++++--- g/net/ghttp/http_server_service.go | 40 +----- geg/net/ghttp/events.go | 2 +- geg/net/ghttp/hello.go | 22 +-- 7 files changed, 283 insertions(+), 157 deletions(-) create mode 100644 g/net/ghttp/http_server_hooks.go diff --git a/g/net/ghttp/http_server.go b/g/net/ghttp/http_server.go index bb6f59ff1..d0672e233 100644 --- a/g/net/ghttp/http_server.go +++ b/g/net/ghttp/http_server.go @@ -31,8 +31,8 @@ const ( // http server结构体 type Server struct { - hmmu sync.RWMutex // handlerMap互斥锁 - htmu sync.RWMutex // handlerTree互斥锁 + hmmu sync.RWMutex // handler互斥锁 + hhmu sync.RWMutex // hooks互斥锁 name string // 服务名称,方便识别 server http.Server // 底层http server对象 config ServerConfig // 配置对象 @@ -40,15 +40,16 @@ type Server struct { methodsMap map[string]bool // 所有支持的HTTP Method(初始化时自动填充) handlerMap HandlerMap // 所有注册的回调函数(静态匹配) handlerTree map[string]interface{} // 所有注册的回调函数(动态匹配,树型+链表优先级匹配) - hooksMap *gmap.StringInterfaceMap // 钩子注册方法map,键值为按照注册顺序生成的glist,用于hook顺序调用 - closeQueue *gqueue.Queue // 请求结束的关闭队列(存放的是需要异步关闭处理的*Request对象) + hooksTree map[string]interface{} // 所有注册的事件回调函数(动态匹配,树型+链表优先级匹配) + handlerCache *gcache.Cache // 服务注册路由内存缓存 + hooksCache *gcache.Cache // 回调事件注册路由内存缓存 servedCount *gtype.Int // 已经服务的请求数(4-8字节,不考虑溢出情况) cookieMaxAge *gtype.Int // Cookie有效期 sessionMaxAge *gtype.Int // Session有效期 sessionIdName *gtype.String // SessionId名称 - routers *gcache.Cache // 服务注册路由内存缓存 cookies *gmap.IntInterfaceMap // 当前服务器正在服务(请求正在执行)的Cookie(每个请求一个Cookie对象) sessions *gcache.Cache // Session内存缓存 + closeQueue *gqueue.Queue // 请求结束的关闭队列(存放的是需要异步关闭处理的*Request对象) } // 域名、URI与回调函数的绑定记录表 @@ -86,16 +87,19 @@ func GetServer(names...string) (*Server) { methodsMap : make(map[string]bool), handlerMap : make(HandlerMap), handlerTree : make(map[string]interface{}), - hooksMap : gmap.NewStringInterfaceMap(), - servedCount : gtype.NewInt(), - closeQueue : gqueue.New(), - routers : gcache.New(), + hooksTree : make(map[string]interface{}), + handlerCache : gcache.New(), + hooksCache : gcache.New(), cookies : gmap.NewIntInterfaceMap(), sessions : gcache.New(), cookieMaxAge : gtype.NewInt(gDEFAULT_COOKIE_MAX_AGE), sessionMaxAge : gtype.NewInt(gDEFAULT_SESSION_MAX_AGE), sessionIdName : gtype.NewString(gDEFAULT_SESSION_ID_NAME), + servedCount : gtype.NewInt(), + closeQueue : gqueue.New(), } + s.hooksCache.SetCap(10000) + s.handlerCache.SetCap(10000) for _, v := range strings.Split(gHTTP_METHODS, ",") { s.methodsMap[v] = true } diff --git a/g/net/ghttp/http_server_handler.go b/g/net/ghttp/http_server_handler.go index 8c9b745a6..e156f01fb 100644 --- a/g/net/ghttp/http_server_handler.go +++ b/g/net/ghttp/http_server_handler.go @@ -17,9 +17,7 @@ import ( "net/http" "path/filepath" "gitee.com/johng/gf/g/os/gfile" - "gitee.com/johng/gf/g/util/gregx" "gitee.com/johng/gf/g/encoding/ghtml" - "gitee.com/johng/gf/g/container/glist" ) // 默认HTTP Server处理入口,http包底层默认使用了gorutine异步处理请求,所以这里不再异步执行 @@ -40,83 +38,6 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) { } } -// 查询请求处理方法 -// 这里有个锁机制,可以并发读,但是不能并发写 -func (s *Server) getHandler(r *Request) *HandlerItem { - handler := s.searchHandler(r) - return handler -} - -// 按照指定hook回调函数的注册顺序进行调用 -func (s *Server)callHookHandler(r *Request, hook string) { - l := s.searchHookHandler(r, hook) - if l != nil { - for _, f := range l { - f(r) - } - } -} - -// 获取指定hook的回调函数列表,按照注册顺序排序 -func (s *Server)searchHookHandler(r *Request, hook string) []HandlerFunc { - domains := []string{gDEFAULT_DOMAIN, strings.Split(r.Host, ":")[0]} - // 首先进行静态匹配 - for _, domain := range domains { - key := s.handlerHookKey(domain, r.Method, r.URL.Path, hook) - if v := s.hooksMap.Get(key); v != nil { - items := v.(*glist.List).FrontAll() - funcs := make([]HandlerFunc, len(items)) - for k, v := range items { - funcs[k] = v.(HandlerFunc) - } - return funcs - } - } - // 其次进行正则匹配(会比较耗效率) - var funcs []HandlerFunc - s.hooksMap.Iterator(func(rule string, list interface{}) bool { - if array, err := gregx.MatchString(`([a-zA-Z]+)\^([a-zA-Z]+):(.+)@([\w\.\-]+)`, rule); len(array) > 3 && err == nil { - // hook匹配 - if !strings.EqualFold(hook, array[1]) { - return true - } - // method匹配 - if !strings.EqualFold(r.Method, array[2]) { - return true - } - // domain匹配 - for _, domain := range domains { - if !strings.EqualFold(domain, array[4]) { - continue - } - // method & domain匹配时,那么执行pattern的正则匹配 - regrule, querystr := s.patternToRegRule(array[3]) - if gregx.IsMatchString(regrule, r.URL.Path) { - // 如果需要query匹配,那么需要重新解析URL - if len(querystr) > 0 { - if query, err := gregx.ReplaceString(regrule, querystr, r.URL.Path); err == nil && len(query) > 0 { - if vals, err := url.ParseQuery(query); err == nil { - for k, v := range vals { - r.values[k] = v - } - } - } - } - // 列表数据解析 - items := list.(*glist.List).FrontAll() - funcs = make([]HandlerFunc, len(items)) - for k, v := range items { - funcs[k] = v.(HandlerFunc) - } - return false - } - } - } - return true - }) - return funcs -} - // 初始化控制器 func (s *Server)callHandler(h *HandlerItem, r *Request) { // 会话处理 diff --git a/g/net/ghttp/http_server_hooks.go b/g/net/ghttp/http_server_hooks.go new file mode 100644 index 000000000..7c57216b2 --- /dev/null +++ b/g/net/ghttp/http_server_hooks.go @@ -0,0 +1,210 @@ +// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://gitee.com/johng/gf. +// 事件回调注册. + +package ghttp + +import ( + "errors" + "strings" + "container/list" + "gitee.com/johng/gf/g/util/gregx" +) + +// hook缓存项,根据URL.Path进行缓存,因此对象中带有缓存参数 +type hookCacheItem struct { + faddr HandlerFunc // 准确的执行方法内存地址 + values map[string][]string // GET解析参数 +} + +// 事件回调注册方法 +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.uri = uri + item.domain = domain + item.method = method + + s.hhmu.Lock() + defer s.hhmu.Unlock() + 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.priority = len(array) + for _, v := range array { + if len(v) == 0 { + continue + } + switch v[0] { + case ':': + fallthrough + case '*': + v = "/" + fallthrough + default: + 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) + return nil + } + } + l.PushBack(item) + + //b,_ := gjson.New(s.hooksTree).ToJsonIndent() + //fmt.Println(string(b)) + + return nil +} + +// 事件回调 - 检索动态路由规则 +// 并按照指定hook回调函数的优先级及注册顺序进行调用 +func (s *Server) callHookHandler(r *Request, hook string) { + var hookItems []*hookCacheItem + cacheKey := hook + "^" + r.URL.Path + if v := s.hooksCache.Get(cacheKey); v == nil { + hookItems = s.searchHookHandler(r, hook) + if hookItems != nil { + s.hooksCache.Set(cacheKey, hookItems, 0) + } + } else { + hookItems = v.([]*hookCacheItem) + } + if hookItems != nil { + for _, item := range hookItems { + for k, v := range item.values { + r.values[k] = v + } + item.faddr(r) + } + } +} + +func (s *Server) searchHookHandler(r *Request, hook string) []*hookCacheItem { + // 一般不会注册事件回调,因此优先判断大小 + if len(s.hooksTree) == 0 { + return nil + } + + s.hhmu.RLock() + defer s.hhmu.RUnlock() + hookItems := make([]*hookCacheItem, 0) + domains := []string{gDEFAULT_DOMAIN, strings.Split(r.Host, ":")[0]} + 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.method, gDEFAULT_METHOD) || strings.EqualFold(item.method, r.Method) { + regrule, names := s.patternToRegRule(item.uri) + if gregx.IsMatchString(regrule, r.URL.Path) { + hookItem := &hookCacheItem {item.faddr, nil} + // 如果需要query匹配,那么需要重新解析URL + if len(names) > 0 { + if match, err := gregx.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设定,参数不区分大小写 +func (s *Server)BindHookHandler(pattern string, hook string, handler HandlerFunc) error { + return s.setHookHandler(pattern, hook, &HandlerItem{ + ctype : nil, + fname : "", + faddr : handler, + }) + return nil +} + +// 通过map批量绑定回调函数 +func (s *Server)BindHookHandlerByMap(pattern string, hookmap map[string]HandlerFunc) error { + for k, v := range hookmap { + if err := s.BindHookHandler(pattern, k, v); err != nil { + return err + } + } + return nil +} + +// 构造用于hooksMap检索的键名 +func (s *Server)handlerHookKey(domain, method, uri, hook string) string { + return strings.ToUpper(hook) + "^" + s.handlerKey(domain, method, uri) +} diff --git a/g/net/ghttp/http_server_router.go b/g/net/ghttp/http_server_router.go index 58f799f32..81d551284 100644 --- a/g/net/ghttp/http_server_router.go +++ b/g/net/ghttp/http_server_router.go @@ -14,8 +14,35 @@ import ( "gitee.com/johng/gf/g/util/gregx" ) +// handler缓存项,根据URL.Path进行缓存,因此对象中带有缓存参数 +type handlerCacheItem struct { + item *HandlerItem // 准确的执行方法内存地址 + values map[string][]string // GET解析参数 +} + +// 查询请求处理方法 +// 这里有个锁机制,可以并发读,但是不能并发写 +func (s *Server) getHandler(r *Request) *HandlerItem { + var handlerItem *handlerCacheItem + if v := s.handlerCache.Get(r.URL.Path); v == nil { + handlerItem = s.searchHandler(r) + if handlerItem != nil { + s.handlerCache.Set(r.URL.Path, handlerItem, 0) + } + } else { + handlerItem = v.(*handlerCacheItem) + } + if handlerItem != nil { + for k, v := range handlerItem.values { + r.values[k] = v + } + return handlerItem.item + } + return nil +} + // 解析pattern -func (s *Server)parsePatternForBindHandler(pattern string) (domain, method, uri string, err error) { +func (s *Server)parsePattern(pattern string) (domain, method, uri string, err error) { uri = pattern domain = gDEFAULT_DOMAIN method = gDEFAULT_METHOD @@ -35,7 +62,7 @@ func (s *Server)parsePatternForBindHandler(pattern string) (domain, method, uri // 注册服务处理方法 func (s *Server) setHandler(pattern string, item *HandlerItem) error { - domain, method, uri, err := s.parsePatternForBindHandler(pattern) + domain, method, uri, err := s.parsePattern(pattern) if err != nil { return errors.New("invalid pattern") } @@ -57,8 +84,6 @@ func (s *Server) setHandler(pattern string, item *HandlerItem) error { // 非叶节点为哈希表检索节点,按照URI注册的层级进行高效检索,直至到叶子链表节点; // 叶子节点是链表,按照优先级进行排序,优先级高的排前面,按照遍历检索,按照哈希表层级检索后的叶子链表一般数据量不大,所以效率比较高; if s.isUriHasRule(uri) { - s.htmu.Lock() - defer s.htmu.Unlock() if _, ok := s.handlerTree[domain]; !ok { s.handlerTree[domain] = make(map[string]interface{}) } @@ -120,42 +145,42 @@ func (s *Server) compareHandlerItemPriority(newItem, oldItem *HandlerItem) bool } // 服务方法检索 -func (s *Server) searchHandler(r *Request) *HandlerItem { - f := s.searchHandlerStatic(r) - if f == nil { - f = s.searchHandlerDynamic(r) +func (s *Server) searchHandler(r *Request) *handlerCacheItem { + item := s.searchHandlerStatic(r) + if item == nil { + item = s.searchHandlerDynamic(r) } // 如果检索不到服务,那么使用默认的"/"服务注册来执行服务 // "/"静态路由是特殊的路由,当所有服务都找不到时,会交给"/"路由规则的控制器来处理 - if f == nil && r.URL.Path != "/" { + if item == nil && r.URL.Path != "/" { path := r.URL.Path r.URL.Path = "/" - f = s.searchHandlerStatic(r) + item = s.searchHandlerStatic(r) r.URL.Path = path } - return f + return item } // 检索静态路由规则 -func (s *Server) searchHandlerStatic(r *Request) *HandlerItem { +func (s *Server) searchHandlerStatic(r *Request) *handlerCacheItem { s.hmmu.RLock() defer s.hmmu.RUnlock() domains := []string{gDEFAULT_DOMAIN, strings.Split(r.Host, ":")[0]} // 首先进行静态匹配 for _, domain := range domains { if f, ok := s.handlerMap[s.handlerKey(domain, r.Method, r.URL.Path)]; ok { - return f + return &handlerCacheItem{f, nil} } } return nil } // 检索动态路由规则 -func (s *Server) searchHandlerDynamic(r *Request) *HandlerItem { +func (s *Server) searchHandlerDynamic(r *Request) *handlerCacheItem { + s.hmmu.RLock() + defer s.hmmu.RUnlock() domains := []string{gDEFAULT_DOMAIN, strings.Split(r.Host, ":")[0]} array := strings.Split(r.URL.Path[1:], "/") - s.htmu.RLock() - defer s.htmu.RLock() for _, domain := range domains { p, ok := s.handlerTree[domain] if !ok { @@ -175,6 +200,8 @@ func (s *Server) searchHandlerDynamic(r *Request) *HandlerItem { lists = append(lists, p.(map[string]interface{})["*list"].(*list.List)) } } + } else { + break } } else { p = p.(map[string]interface{})[v] @@ -193,18 +220,20 @@ func (s *Server) searchHandlerDynamic(r *Request) *HandlerItem { if strings.EqualFold(item.method, gDEFAULT_METHOD) || strings.EqualFold(item.method, r.Method) { regrule, names := s.patternToRegRule(item.uri) if gregx.IsMatchString(regrule, r.URL.Path) { + handlerItem := &handlerCacheItem{item, nil} // 如果需要query匹配,那么需要重新解析URL if len(names) > 0 { if match, err := gregx.MatchString(regrule, r.URL.Path); err == nil { array := strings.Split(names, ",") if len(match) > len(array) { + handlerItem.values = make(map[string][]string) for index, name := range array { - r.values[name] = []string{match[index + 1]} + handlerItem.values[name] = []string{match[index + 1]} } } } } - return item + return handlerItem } } } diff --git a/g/net/ghttp/http_server_service.go b/g/net/ghttp/http_server_service.go index 1c0052eff..b5dbb40f7 100644 --- a/g/net/ghttp/http_server_service.go +++ b/g/net/ghttp/http_server_service.go @@ -196,42 +196,4 @@ func (s *Server)BindControllerMethod(pattern string, c Controller, methods strin } } return s.bindHandlerByMap(m) -} - -// 绑定指定的hook回调函数, pattern参数同BindHandler,支持命名路由;hook参数的值由ghttp server设定,参数不区分大小写 -func (s *Server)BindHookHandler(pattern string, hook string, handler HandlerFunc) error { - //domain, method, uri, err := s.parsePatternForBindHookHandler(pattern) - //if err != nil { - // return errors.New("invalid pattern") - //} - //var l *glist.List - //if method == gDEFAULT_METHOD { - // for v, _ := range s.methodsMap { - // if v := s.hooksMap.GetWithDefault(s.handlerHookKey(domain, v, uri, hook), glist.New()); v != nil { - // l = v.(*glist.List) - // } - // l.PushBack(handler) - // } - //} else { - // if v := s.hooksMap.GetWithDefault(s.handlerHookKey(domain, method, uri, hook), glist.New()); v == nil { - // l = v.(*glist.List) - // } - // l.PushBack(handler) - //} - return nil -} - -// 通过map批量绑定回调函数 -func (s *Server)BindHookHandlerByMap(pattern string, hookmap map[string]HandlerFunc) error { - for k, v := range hookmap { - if err := s.BindHookHandler(pattern, k, v); err != nil { - return err - } - } - return nil -} - -// 构造用于hooksMap检索的键名 -func (s *Server)handlerHookKey(domain, method, uri, hook string) string { - return strings.ToUpper(hook) + "^" + s.handlerKey(domain, method, uri) -} +} \ No newline at end of file diff --git a/geg/net/ghttp/events.go b/geg/net/ghttp/events.go index f62646f8f..74beffdef 100644 --- a/geg/net/ghttp/events.go +++ b/geg/net/ghttp/events.go @@ -6,7 +6,7 @@ import ( ) func main() { - pattern := "/" + pattern := "/:name/action" ghttp.GetServer().BindHookHandlerByMap(pattern, map[string]ghttp.HandlerFunc{ "BeforeServe" : func(r *ghttp.Request){ fmt.Println("BeforeServe") }, "AfterServe" : func(r *ghttp.Request){ fmt.Println("AfterServe") }, diff --git a/geg/net/ghttp/hello.go b/geg/net/ghttp/hello.go index 59753b53a..5cb869e97 100644 --- a/geg/net/ghttp/hello.go +++ b/geg/net/ghttp/hello.go @@ -7,19 +7,19 @@ func main() { r.Response.WriteString("hello world") }) - //ghttp.GetServer().BindHandler("/:name/*any", func(r *ghttp.Request) { - // r.Response.WriteString("any") + ghttp.GetServer().BindHandler("/:name/*any", func(r *ghttp.Request) { + r.Response.WriteString("any") + r.Response.WriteString(r.GetQueryString("name")) + r.Response.WriteString(r.GetQueryString("any")) + }) + //ghttp.GetServer().BindHandler("/:name/action", func(r *ghttp.Request) { // r.Response.WriteString(r.GetQueryString("name")) - // r.Response.WriteString(r.GetQueryString("any")) - //}) - ////ghttp.GetServer().BindHandler("/:name/action", func(r *ghttp.Request) { - //// r.Response.WriteString(r.GetQueryString("name")) - ////}) - //ghttp.GetServer().BindHandler("/:name/:action/:aaa", func(r *ghttp.Request) { - // r.Response.WriteString("name") - // r.Response.WriteString(r.GetQueryString("name")) - // r.Response.WriteString(r.GetQueryString("action")) //}) + ghttp.GetServer().BindHandler("/:name/:action/:aaa", func(r *ghttp.Request) { + r.Response.WriteString("name") + r.Response.WriteString(r.GetQueryString("name")) + r.Response.WriteString(r.GetQueryString("action")) + }) ghttp.GetServer().SetPort(10000) ghttp.GetServer().Run() } \ No newline at end of file