ghttp路由功能改进中

This commit is contained in:
john
2018-08-02 15:23:14 +08:00
parent 9ea5004859
commit 3a7d4c782e
2 changed files with 43 additions and 102 deletions

View File

@ -27,7 +27,6 @@ import (
"github.com/gorilla/websocket"
"gitee.com/johng/gf/g/os/gtime"
"time"
"container/list"
)
const (
@ -43,6 +42,8 @@ const (
gDEFAULT_COOKIE_MAX_AGE = 86400*365 // 默认cookie有效期(一年)
gDEFAULT_SESSION_MAX_AGE = 600 // 默认session有效期(600秒)
gDEFAULT_SESSION_ID_NAME = "gfsessionid" // 默认存放Cookie中的SessionId名称
gSERVE_CACHE_LRU_SIZE = 100000 // 服务回调函数缓存LRU大小
gHOOKS_CACHE_LRU_SIZE = 100000 // 事件回调函数缓存LRU大小
)
// ghttp.Server结构体
@ -56,8 +57,9 @@ type Server struct {
servedCount *gtype.Int // 已经服务的请求数(4-8字节不考虑溢出情况)同时作为请求ID
closeQueue *gqueue.Queue // 请求结束的关闭队列(存放的是需要异步关闭处理的*Request对象)
// 服务注册相关
handlerTree map[string]interface{} // 所有注册的回调函数(路由表,树型结构,哈希表+链表优先级匹配)
handlerCache *gcache.Cache // 服务注册路由内存缓存
serveTree map[string]interface{} // 所有注册的服务回调函数(路由表,树型结构,哈希表+链表优先级匹配)
hooksTree map[string]interface{} // 所有注册的事件回调函数(路由表,树型结构,哈希表+链表优先级匹配)
serveCache *gcache.Cache // 服务注册路由内存缓存
hooksCache *gcache.Cache // 事件回调路由内存缓存
// 自定义状态码回调
hsmu sync.RWMutex // status handler互斥锁
@ -88,7 +90,7 @@ type Router struct {
Priority int // 优先级,用于链表排序,值越大优先级越高
}
// 域名、URI与回调函数的绑定记录表
// pattern与回调函数的绑定map
type handlerMap map[string]*handlerItem
// http回调函数注册信息
@ -96,19 +98,13 @@ type handlerItem struct {
ctype reflect.Type // 控制器类型(反射类型)
fname string // 回调方法名称
faddr HandlerFunc // 准确的执行方法内存地址(与以上两个参数二选一)
}
// 路由注册项(这里使用了非并发安全的list.List因为该对象的使用统一是由htmu互斥锁保证并发安全)
type handlerRegisterItem struct {
handler *handlerItem // 准确的执行方法内存地址
hooks map[string]*list.List // 当前的事件回调注册,键名为事件名称,键值为事件执行方法链表
router *Router // 注册时绑定的路由对象
router *Router // 注册时绑定的路由对象
}
// 根据特定URL.Path解析后的路由检索结果项
type handlerParsedItem struct {
item *handlerRegisterItem // 路由注册项
values map[string][]string // 特定URL.Path的Router解析参数
item *handlerItem // 路由注册项
values map[string][]string // 特定URL.Path的Router解析参数
}
// HTTP注册函数
@ -172,8 +168,9 @@ func GetServer(name...interface{}) (*Server) {
servers : make([]*gracefulServer, 0),
methodsMap : make(map[string]bool),
statusHandlerMap : make(map[string]HandlerFunc),
handlerTree : make(map[string]interface{}),
handlerCache : gcache.New(),
serveTree : make(map[string]interface{}),
hooksTree : make(map[string]interface{}),
serveCache : gcache.New(),
hooksCache : gcache.New(),
cookies : gmap.NewIntInterfaceMap(),
sessions : gcache.New(),
@ -192,8 +189,8 @@ func GetServer(name...interface{}) (*Server) {
s.errorLogger.SetBacktraceSkip(4)
s.accessLogger.SetBacktraceSkip(4)
// 设置路由解析缓存上限使用LRU进行缓存淘汰
s.hooksCache.SetCap(10000)
s.handlerCache.SetCap(10000)
s.serveCache.SetCap(gSERVE_CACHE_LRU_SIZE)
s.hooksCache.SetCap(gHOOKS_CACHE_LRU_SIZE)
for _, v := range strings.Split(gHTTP_METHODS, ",") {
s.methodsMap[v] = true
}

View File

@ -70,7 +70,7 @@ func (s *Server)parsePattern(pattern string) (domain, method, uri string, err er
func (s *Server) setHandler(pattern string, handler *handlerItem, hook ... string) error {
// Web Server正字运行时无法动态注册路由方法
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("cannnot bind handler while server running")
return errors.New("cannot bind handler while server running")
}
var hookName string
if len(hook) > 0 {
@ -82,34 +82,28 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ... strin
}
// 路由对象
router := &Router {
handler.router = &Router {
Uri : uri,
Domain : domain,
Method : method,
Priority : strings.Count(uri[1:], "/"),
}
router.RegRule, router.RegNames = s.patternToRegRule(uri)
// 注册对象
registerItem := &handlerRegisterItem {
handler : handler,
hooks : make(map[string]*list.List),
router : router,
}
if len(hookName) > 0 {
registerItem.handler = nil
registerItem.hooks[hookName] = list.New()
registerItem.hooks[hookName].PushBack(handler)
}
handler.router.RegRule, handler.router.RegNames = s.patternToRegRule(uri)
// 动态注册,首先需要判断是否是动态注册,如果不是那么就没必要添加到动态注册记录变量中。
// 非叶节点为哈希表检索节点按照URI注册的层级进行高效检索直至到叶子链表节点
// 叶子节点是链表,按照优先级进行排序,优先级高的排前面,按照遍历检索,按照哈希表层级检索后的叶子链表数据量不会很大,所以效率比较高;
if _, ok := s.handlerTree[domain]; !ok {
s.handlerTree[domain] = make(map[string]interface{})
tree := (map[string]interface{})(nil)
if len(hookName) == 0 {
tree = s.serveTree
} else {
tree = s.hooksTree
}
if _, ok := tree[domain]; !ok {
tree[domain] = make(map[string]interface{})
}
// 用于遍历的指针
p := s.handlerTree[domain]
p := tree[domain]
// 当前节点的规则链表
lists := make([]*list.List, 0)
array := ([]string)(nil)
@ -153,79 +147,29 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ... strin
}
// 得到的lists是该路由规则一路匹配下来相关的模糊匹配链表(注意不是这棵树所有的链表)
// 从头开始遍历每个节点的模糊匹配链表,将该路由项插入进去(按照优先级高的放在前面)
item := (*handlerRegisterItem)(nil)
// 用以标记 *handlerRegisterItem 指向的对象是否已经处理过,因为多个节点可能会关联同一个该对象
pushedItemSet := gset.NewStringSet()
if len(hookName) == 0 {
// 普通方法路由注册,追加或者覆盖
for _, l := range lists {
pushed := false
address := ""
for e := l.Front(); e != nil; e = e.Next() {
item = e.Value.(*handlerRegisterItem)
address = fmt.Sprintf("%p", item)
if pushedItemSet.Contains(address) {
pushed = true
break
}
// 判断是否已存在相同的路由注册项
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)
item := (*handlerItem)(nil)
for _, l := range lists {
pushed := false
for e := l.Front(); e != nil; e = e.Next() {
item = e.Value.(*handlerItem)
// 判断是否已存在相同的路由注册项
if len(hookName) == 0 {
if strings.EqualFold(handler.router.Domain, item.router.Domain) &&
strings.EqualFold(handler.router.Method, item.router.Method) &&
strings.EqualFold(handler.router.Uri, item.router.Uri) {
e.Value = handler
pushed = true
break
}
}
if pushed {
if len(address) > 0 {
pushedItemSet.Add(address)
}
} else {
l.PushBack(registerItem)
if s.compareRouterPriority(handler.router, item.router) {
l.InsertBefore(handler, e)
pushed = true
break
}
}
} else {
// 回调方法路由注册,将方法追加到链表末尾
for _, l := range lists {
pushed := false
address := ""
for e := l.Front(); e != nil; e = e.Next() {
item = e.Value.(*handlerRegisterItem)
address = fmt.Sprintf("%p", item)
if pushedItemSet.Contains(address) {
pushed = true
break
}
// 判断是否已存在相同的路由注册项
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] = list.New()
}
item.hooks[hookName].PushBack(handler)
pushed = true
break
}
if s.compareRouterPriority(router, item.router) {
l.InsertBefore(registerItem, e)
pushed = true
break
}
}
if pushed {
if len(address) > 0 {
pushedItemSet.Add(address)
}
} else {
l.PushBack(registerItem)
}
if !pushed {
l.PushBack(handler)
}
}
//gutil.Dump(s.handlerTree)