mirror of
https://gitee.com/johng/gf
synced 2026-06-06 16:21:40 +08:00
ghttp路由功能改进中
This commit is contained in:
@ -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注册函数
|
||||
|
||||
@ -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设定,参数不区分大小写
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
ghttp.
|
||||
gfile.PutContentsAppend("/tmp/test", "1")
|
||||
gfile.PutContentsAppend("/tmp/test", "2")
|
||||
gfile.PutContentsAppend("/tmp/test", "3")
|
||||
|
||||
Reference in New Issue
Block a user