github.com/zhongdalu/gf@v1.0.0/g/net/ghttp/ghttp_server_router.go (about) 1 // Copyright 2018 gf Author(https://github.com/zhongdalu/gf). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/zhongdalu/gf. 6 // 路由控制基本方法. 7 8 package ghttp 9 10 import ( 11 "container/list" 12 "errors" 13 "fmt" 14 "github.com/zhongdalu/gf/g/os/glog" 15 "github.com/zhongdalu/gf/g/text/gregex" 16 "github.com/zhongdalu/gf/g/text/gstr" 17 "runtime" 18 "strings" 19 ) 20 21 // 解析pattern 22 func (s *Server) parsePattern(pattern string) (domain, method, path string, err error) { 23 path = strings.TrimSpace(pattern) 24 domain = gDEFAULT_DOMAIN 25 method = gDEFAULT_METHOD 26 if array, err := gregex.MatchString(`([a-zA-Z]+):(.+)`, pattern); len(array) > 1 && err == nil { 27 path = strings.TrimSpace(array[2]) 28 if v := strings.TrimSpace(array[1]); v != "" { 29 method = v 30 } 31 } 32 if array, err := gregex.MatchString(`(.+)@([\w\.\-]+)`, path); len(array) > 1 && err == nil { 33 path = strings.TrimSpace(array[1]) 34 if v := strings.TrimSpace(array[2]); v != "" { 35 domain = v 36 } 37 } 38 if path == "" { 39 err = errors.New("invalid pattern: URI should not be empty") 40 } 41 // 去掉末尾的"/"符号,与路由匹配时处理一致 42 if path != "/" { 43 path = strings.TrimRight(path, "/") 44 } 45 return 46 } 47 48 // 获得服务注册的文件地址信息 49 func (s *Server) getHandlerRegisterCallerLine(handler *handlerItem) string { 50 skip := 5 51 if handler.rtype == gROUTE_REGISTER_HANDLER { 52 skip = 4 53 } 54 if _, cfile, cline, ok := runtime.Caller(skip); ok { 55 return fmt.Sprintf("%s:%d", cfile, cline) 56 } 57 return "" 58 } 59 60 // 路由注册处理方法。 61 // 如果带有hook参数,表示是回调注册方法; 否则为普通路由执行方法。 62 func (s *Server) setHandler(pattern string, handler *handlerItem, hook ...string) { 63 // Web Server正常运行时无法动态注册路由方法 64 if s.Status() == SERVER_STATUS_RUNNING { 65 glog.Error("cannot bind handler while server running") 66 return 67 } 68 var hookName string 69 if len(hook) > 0 { 70 hookName = hook[0] 71 } 72 domain, method, uri, err := s.parsePattern(pattern) 73 if err != nil { 74 glog.Error("invalid pattern:", pattern, err) 75 return 76 } 77 if len(uri) == 0 || uri[0] != '/' { 78 glog.Error("invalid pattern:", pattern, "URI should lead with '/'") 79 return 80 } 81 // 注册地址记录及重复注册判断 82 regkey := s.handlerKey(hookName, method, uri, domain) 83 caller := s.getHandlerRegisterCallerLine(handler) 84 if len(hook) == 0 { 85 if item, ok := s.routesMap[regkey]; ok { 86 glog.Errorf(`duplicated route registry "%s", already registered at %s`, pattern, item[0].file) 87 return 88 } 89 } 90 91 // 路由对象 92 handler.router = &Router{ 93 Uri: uri, 94 Domain: domain, 95 Method: method, 96 Priority: strings.Count(uri[1:], "/"), 97 } 98 handler.router.RegRule, handler.router.RegNames = s.patternToRegRule(uri) 99 100 // 动态注册,首先需要判断是否是动态注册,如果不是那么就没必要添加到动态注册记录变量中。 101 // 非叶节点为哈希表检索节点,按照URI注册的层级进行高效检索,直至到叶子链表节点; 102 // 叶子节点是链表,按照优先级进行排序,优先级高的排前面,按照遍历检索,按照哈希表层级检索后的叶子链表数据量不会很大,所以效率比较高; 103 tree := (map[string]interface{})(nil) 104 if len(hookName) == 0 { 105 tree = s.serveTree 106 } else { 107 tree = s.hooksTree 108 } 109 if _, ok := tree[domain]; !ok { 110 tree[domain] = make(map[string]interface{}) 111 } 112 // 用于遍历的指针 113 p := tree[domain] 114 if len(hookName) > 0 { 115 if _, ok := p.(map[string]interface{})[hookName]; !ok { 116 p.(map[string]interface{})[hookName] = make(map[string]interface{}) 117 } 118 p = p.(map[string]interface{})[hookName] 119 } 120 // 当前节点的规则链表 121 lists := make([]*list.List, 0) 122 array := ([]string)(nil) 123 if strings.EqualFold("/", uri) { 124 array = []string{"/"} 125 } else { 126 array = strings.Split(uri[1:], "/") 127 } 128 // 键名"*fuzz"代表模糊匹配节点,其下会有一个链表; 129 // 键名"*list"代表链表,叶子节点和模糊匹配节点都有该属性; 130 for k, v := range array { 131 if len(v) == 0 { 132 continue 133 } 134 // 判断是否模糊匹配规则 135 if gregex.IsMatchString(`^[:\*]|\{[\w\.\-]+\}|\*`, v) { 136 v = "*fuzz" 137 // 由于是模糊规则,因此这里会有一个*list,用以将后续的路由规则加进来, 138 // 检索会从叶子节点的链表往根节点按照优先级进行检索 139 if v, ok := p.(map[string]interface{})["*list"]; !ok { 140 p.(map[string]interface{})["*list"] = list.New() 141 lists = append(lists, p.(map[string]interface{})["*list"].(*list.List)) 142 } else { 143 lists = append(lists, v.(*list.List)) 144 } 145 } 146 // 属性层级数据写入 147 if _, ok := p.(map[string]interface{})[v]; !ok { 148 p.(map[string]interface{})[v] = make(map[string]interface{}) 149 } 150 p = p.(map[string]interface{})[v] 151 // 到达叶子节点,往list中增加匹配规则(条件 v != "*fuzz" 是因为模糊节点的话在前面已经添加了*list链表) 152 if k == len(array)-1 && v != "*fuzz" { 153 if v, ok := p.(map[string]interface{})["*list"]; !ok { 154 p.(map[string]interface{})["*list"] = list.New() 155 lists = append(lists, p.(map[string]interface{})["*list"].(*list.List)) 156 } else { 157 lists = append(lists, v.(*list.List)) 158 } 159 } 160 } 161 // 上面循环后得到的lists是该路由规则一路匹配下来相关的模糊匹配链表(注意不是这棵树所有的链表)。 162 // 下面从头开始遍历每个节点的模糊匹配链表,将该路由项插入进去(按照优先级高的放在lists链表的前面) 163 item := (*handlerItem)(nil) 164 for _, l := range lists { 165 pushed := false 166 for e := l.Front(); e != nil; e = e.Next() { 167 item = e.Value.(*handlerItem) 168 // 判断是否已存在相同的路由注册项,(如果不是hook注册)是则进行替换 169 if len(hookName) == 0 { 170 if strings.EqualFold(handler.router.Domain, item.router.Domain) && 171 strings.EqualFold(handler.router.Method, item.router.Method) && 172 strings.EqualFold(handler.router.Uri, item.router.Uri) { 173 e.Value = handler 174 pushed = true 175 break 176 } 177 } 178 // 如果路由注册项不相等,那么判断优先级,决定插入顺序 179 if s.compareRouterPriority(handler.router, item.router) { 180 l.InsertBefore(handler, e) 181 pushed = true 182 break 183 } 184 } 185 if !pushed { 186 l.PushBack(handler) 187 } 188 } 189 // gutil.Dump(s.serveTree) 190 // gutil.Dump(s.hooksTree) 191 if _, ok := s.routesMap[regkey]; !ok { 192 s.routesMap[regkey] = make([]registeredRouteItem, 0) 193 } 194 s.routesMap[regkey] = append(s.routesMap[regkey], registeredRouteItem{ 195 file: caller, 196 handler: handler, 197 }) 198 } 199 200 // 对比两个handlerItem的优先级,需要非常注意的是,注意新老对比项的参数先后顺序。 201 // 返回值true表示newRouter优先级比oldRouter高,会被添加链表中oldRouter的前面;否则后面。 202 // 优先级比较规则: 203 // 1、层级越深优先级越高(对比/数量); 204 // 2、模糊规则优先级:{xxx} > :xxx > *xxx; 205 func (s *Server) compareRouterPriority(newRouter, oldRouter *Router) bool { 206 // 优先比较层级,层级越深优先级越高 207 if newRouter.Priority > oldRouter.Priority { 208 return true 209 } 210 if newRouter.Priority < oldRouter.Priority { 211 return false 212 } 213 // 精准匹配比模糊匹配规则优先级高,例如:/name/act 比 /{name}/:act 优先级高 214 var fuzzyCountFieldNew, fuzzyCountFieldOld int 215 var fuzzyCountNameNew, fuzzyCountNameOld int 216 var fuzzyCountAnyNew, fuzzyCountAnyOld int 217 var fuzzyCountTotalNew, fuzzyCountTotalOld int 218 for _, v := range newRouter.Uri { 219 switch v { 220 case '{': 221 fuzzyCountFieldNew++ 222 case ':': 223 fuzzyCountNameNew++ 224 case '*': 225 fuzzyCountAnyNew++ 226 } 227 } 228 for _, v := range oldRouter.Uri { 229 switch v { 230 case '{': 231 fuzzyCountFieldOld++ 232 case ':': 233 fuzzyCountNameOld++ 234 case '*': 235 fuzzyCountAnyOld++ 236 } 237 } 238 fuzzyCountTotalNew = fuzzyCountFieldNew + fuzzyCountNameNew + fuzzyCountAnyNew 239 fuzzyCountTotalOld = fuzzyCountFieldOld + fuzzyCountNameOld + fuzzyCountAnyOld 240 if fuzzyCountTotalNew < fuzzyCountTotalOld { 241 return true 242 } 243 if fuzzyCountTotalNew > fuzzyCountTotalOld { 244 return false 245 } 246 247 /** 如果模糊规则数量相等,那么执行分别的数量判断 **/ 248 249 // 例如:/name/{act} 比 /name/:act 优先级高 250 if fuzzyCountFieldNew > fuzzyCountFieldOld { 251 return true 252 } 253 if fuzzyCountFieldNew < fuzzyCountFieldOld { 254 return false 255 } 256 // 例如: /name/:act 比 /name/*act 优先级高 257 if fuzzyCountNameNew > fuzzyCountNameOld { 258 return true 259 } 260 if fuzzyCountNameNew < fuzzyCountNameOld { 261 return false 262 } 263 264 /* 模糊规则数量相等,后续不用再判断*规则的数量比较了 */ 265 266 // 比较HTTP METHOD,更精准的优先级更高 267 if newRouter.Method != gDEFAULT_METHOD { 268 return true 269 } 270 if oldRouter.Method != gDEFAULT_METHOD { 271 return true 272 } 273 274 // 最后新的规则比旧的规则优先级低 275 return false 276 } 277 278 // 将pattern(不带method和domain)解析成正则表达式匹配以及对应的query字符串 279 func (s *Server) patternToRegRule(rule string) (regrule string, names []string) { 280 if len(rule) < 2 { 281 return rule, nil 282 } 283 regrule = "^" 284 array := strings.Split(rule[1:], "/") 285 for _, v := range array { 286 if len(v) == 0 { 287 continue 288 } 289 switch v[0] { 290 case ':': 291 if len(v) > 1 { 292 regrule += `/([^/]+)` 293 names = append(names, v[1:]) 294 break 295 } else { 296 regrule += `/[^/]+` 297 break 298 } 299 case '*': 300 if len(v) > 1 { 301 regrule += `/{0,1}(.*)` 302 names = append(names, v[1:]) 303 break 304 } else { 305 regrule += `/{0,1}.*` 306 break 307 } 308 default: 309 // 特殊字符替换 310 v = gstr.ReplaceByMap(v, map[string]string{ 311 `.`: `\.`, 312 `+`: `\+`, 313 `*`: `.*`, 314 }) 315 s, _ := gregex.ReplaceStringFunc(`\{[\w\.\-]+\}`, v, func(s string) string { 316 names = append(names, s[1:len(s)-1]) 317 return `([^/]+)` 318 }) 319 if strings.EqualFold(s, v) { 320 regrule += "/" + v 321 } else { 322 regrule += "/" + s 323 } 324 } 325 } 326 regrule += `$` 327 return 328 }