ghttp对象路由注册增加Init&Shut自动回调方法;ghttp增加重复路由注册检测功能;gfsnotify增加默认递归Add/Remove特性

This commit is contained in:
John
2018-08-19 11:25:15 +08:00
parent 1b69184999
commit 85157c8708
21 changed files with 322 additions and 69 deletions

1
TODO
View File

@ -18,6 +18,7 @@ orm增加sqlite对Save方法的支持(去掉触发器语句);
ghttp.Server的Cookie及Session锁机制优化(去掉map锁机制);
ghttp.Server增加Ip访问控制功能(DenyIps&AllowIps)
ghttp路由功能增加分组路由特性
解决glog串日志情况
DONE:

View File

@ -50,6 +50,9 @@ const (
gDEFAULT_SESSION_ID_NAME = "gfsessionid" // 默认存放Cookie中的SessionId名称
gSERVE_CACHE_LRU_SIZE = 100000 // 服务回调函数缓存LRU大小
gHOOKS_CACHE_LRU_SIZE = 100000 // 事件回调函数缓存LRU大小
gROUTE_REGISTER_HANDLER = 1
gROUTE_REGISTER_OBJECT = 2
gROUTE_REGISTER_CONTROLLER = 3
)
// ghttp.Server结构体
@ -67,6 +70,7 @@ type Server struct {
hooksTree map[string]interface{} // 所有注册的事件回调函数(路由表,树型结构,哈希表+链表优先级匹配)
serveCache *gcache.Cache // 服务注册路由内存缓存
hooksCache *gcache.Cache // 事件回调路由内存缓存
routesMap map[string]string // 已经注册的路由及对应的注册方法文件地址
// 自定义状态码回调
hsmu sync.RWMutex // status handler互斥锁
statusHandlerMap map[string]HandlerFunc // 不同状态码下的注册处理方法(例如404状态时的处理方法)
@ -103,6 +107,7 @@ type handlerMap map[string]*handlerItem
// http回调函数注册信息
type handlerItem struct {
rtype int // 注册方式
ctype reflect.Type // 控制器类型(反射类型)
fname string // 回调方法名称
faddr HandlerFunc // 准确的执行方法内存地址(与以上两个参数二选一)
@ -182,6 +187,7 @@ func GetServer(name...interface{}) (*Server) {
hooksTree : make(map[string]interface{}),
serveCache : gcache.New(),
hooksCache : gcache.New(),
routesMap : make(map[string]string),
cookies : gmap.NewIntInterfaceMap(),
sessions : gcache.New(),
servedCount : gtype.NewInt(),

View File

@ -41,11 +41,7 @@ func (s *Server) Domain(domains string) *Domain {
// 注意该方法是直接绑定方法的内存地址,执行的时候直接执行该方法,不会存在初始化新的控制器逻辑
func (d *Domain) BindHandler(pattern string, handler HandlerFunc) error {
for domain, _ := range d.m {
if err := d.s.bindHandlerItem(pattern + "@" + domain, &handlerItem{
ctype : nil,
fname : "",
faddr : handler,
}); err != nil {
if err := d.s.BindHandler(pattern + "@" + domain, handler); err != nil {
return err
}
}
@ -66,9 +62,9 @@ func (d *Domain) BindObject(pattern string, obj interface{}, methods...string) e
}
// 执行对象方法注册methods参数不区分大小写
func (d *Domain) BindObjectMethod(pattern string, obj interface{}, methods string) error {
func (d *Domain) BindObjectMethod(pattern string, obj interface{}, method string) error {
for domain, _ := range d.m {
if err := d.s.BindObjectMethod(pattern + "@" + domain, obj, methods); err != nil {
if err := d.s.BindObjectMethod(pattern + "@" + domain, obj, method); err != nil {
return err
}
}
@ -98,20 +94,20 @@ func (d *Domain) BindController(pattern string, c Controller, methods...string)
return nil
}
// RESTful控制器注册
func (d *Domain) BindControllerRest(pattern string, c Controller) error {
// 控制器方法注册methods参数区分大小写
func (d *Domain) BindControllerMethod(pattern string, c Controller, method string) error {
for domain, _ := range d.m {
if err := d.s.BindControllerRest(pattern + "@" + domain, c); err != nil {
if err := d.s.BindControllerMethod(pattern + "@" + domain, c, method); err != nil {
return err
}
}
return nil
}
// 控制器方法注册methods参数区分大小写
func (d *Domain) BindControllerMethod(pattern string, c Controller, methods string) error {
// RESTful控制器注册
func (d *Domain) BindControllerRest(pattern string, c Controller) error {
for domain, _ := range d.m {
if err := d.s.BindControllerMethod(pattern + "@" + domain, c, methods); err != nil {
if err := d.s.BindControllerRest(pattern + "@" + domain, c); err != nil {
return err
}
}

View File

@ -110,7 +110,16 @@ func (s *Server)callServeHandler(h *handlerItem, r *Request) {
c.MethodByName("Shut").Call([]reflect.Value{reflect.ValueOf(r)})
}
} else {
h.faddr(r)
// 是否有初始化及完成回调方法
if h.finit != nil {
h.finit(r)
}
if !r.IsExited() {
h.faddr(r)
if h.fshut != nil {
h.fshut(r)
}
}
}
}

View File

@ -13,6 +13,9 @@ import (
"container/list"
"gitee.com/johng/gf/g/util/gregex"
"gitee.com/johng/gf/g/util/gstr"
"gitee.com/johng/gf/g/os/glog"
"fmt"
"runtime"
)
@ -39,13 +42,38 @@ func (s *Server)parsePattern(pattern string) (domain, method, uri string, err er
return
}
// 获得服务注册的文件地址信息
func (s *Server) getHandlerRegisterCallerLine(handler *handlerItem) string {
skip := 5
if handler.rtype == gROUTE_REGISTER_HANDLER {
skip = 4
}
if _, cfile, cline, ok := runtime.Caller(skip); ok {
return fmt.Sprintf("%s:%d", cfile, cline)
}
return ""
}
// 路由注册处理方法。
// 如果带有hook参数表示是回调注册方法否则为普通路由执行方法。
func (s *Server) setHandler(pattern string, handler *handlerItem, hook ... string) error {
func (s *Server) setHandler(pattern string, handler *handlerItem, hook ... string) (resultErr error) {
// Web Server正字运行时无法动态注册路由方法
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("cannot bind handler while server running")
}
caller := s.getHandlerRegisterCallerLine(handler)
if line, ok := s.routesMap[pattern]; ok {
s := fmt.Sprintf(`duplicated route registry "%s" in %s , former in %s`, pattern, caller, line)
glog.Errorfln(s)
return errors.New(s)
} else {
defer func() {
if resultErr == nil {
s.routesMap[pattern] = caller
}
}()
}
var hookName string
if len(hook) > 0 {
hookName = hook[0]
@ -132,13 +160,13 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ... strin
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
pushed = true
break
}
}
@ -157,7 +185,7 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ... strin
return nil
}
// 对比两个handlerItem的优先级需要非常注意的是注意新老对比项的参数先后顺序
// 对比两个handlerItem的优先级需要非常注意的是注意新老对比项的参数先后顺序
// 优先级比较规则:
// 1、层级越深优先级越高(对比/数量)
// 2、模糊规则优先级{xxx} > :xxx > *xxx
@ -168,19 +196,66 @@ func (s *Server) compareRouterPriority(newRouter, oldRouter *Router) bool {
if newRouter.Priority < oldRouter.Priority {
return false
}
// 例如:/{user}/{act} 比 /:user/:act 优先级高
if strings.Count(newRouter.Uri, "{") > strings.Count(oldRouter.Uri, "{") {
// 精准匹配比模糊匹配规则优先级高,例如:/name/act 比 /{name}/:act 优先级高
var fuzzyCountFieldNew, fuzzyCountFieldOld int
var fuzzyCountNameNew, fuzzyCountNameOld int
var fuzzyCountAnyNew, fuzzyCountAnyOld int
var fuzzyCountTotalNew, fuzzyCountTotalOld int
for _, v := range newRouter.Uri {
switch v {
case '{':
fuzzyCountFieldNew++
case ':':
fuzzyCountNameNew++
case '*':
fuzzyCountAnyNew++
}
}
for _, v := range oldRouter.Uri {
switch v {
case '{':
fuzzyCountFieldOld++
case ':':
fuzzyCountNameOld++
case '*':
fuzzyCountAnyOld++
}
}
fuzzyCountTotalNew = fuzzyCountFieldNew + fuzzyCountNameNew + fuzzyCountAnyNew
fuzzyCountTotalOld = fuzzyCountFieldOld + fuzzyCountNameOld + fuzzyCountAnyOld
if fuzzyCountTotalNew < fuzzyCountTotalOld {
return true
}
// 例如: /:name/update 比 /:name/:action优先级高
if strings.Count(newRouter.Uri, "/:") < strings.Count(oldRouter.Uri, "/:") {
// 例如: /:name/:action 比 /:name/*any 优先级高
if strings.Count(newRouter.Uri, "/*") < strings.Count(oldRouter.Uri, "/*") {
return true
}
if fuzzyCountTotalNew > fuzzyCountTotalOld {
return false
}
return false
/** 如果模糊规则数量相等,那么执行分别的数量判断 **/
// 例如:/name/{act} 比 /name/:act 优先级高
if fuzzyCountFieldNew > fuzzyCountFieldOld {
return true
}
if fuzzyCountFieldNew < fuzzyCountFieldOld {
return false
}
// 例如: /name/:act 比 /name/*act 优先级高
if fuzzyCountNameNew > fuzzyCountNameOld {
return true
}
if fuzzyCountNameNew < fuzzyCountNameOld {
return false
}
// 比较HTTP METHOD更精准的优先级更高
if newRouter.Method != gDEFAULT_METHOD {
return true
}
if oldRouter.Method != gDEFAULT_METHOD {
return true
}
// 模糊规则数量相等,后续不用再判断*规则的数量比较了,
// 这种情况下新的规则比旧的规则优先级更高
return true
}
// 将pattern不带method和domain解析成正则表达式匹配以及对应的query字符串

View File

@ -39,6 +39,7 @@ func (s *Server)BindController(pattern string, c Controller, methods...string) e
}
key := s.mergeBuildInNameToPattern(pattern, sname, mname, true)
m[key] = &handlerItem {
rtype : gROUTE_REGISTER_CONTROLLER,
ctype : v.Elem().Type(),
fname : mname,
faddr : nil,
@ -53,6 +54,7 @@ func (s *Server)BindController(pattern string, c Controller, methods...string) e
}
}
m[p] = &handlerItem {
rtype : gROUTE_REGISTER_CONTROLLER,
ctype : v.Elem().Type(),
fname : mname,
faddr : nil,
@ -75,6 +77,7 @@ func (s *Server)BindControllerMethod(pattern string, c Controller, method string
}
key := s.mergeBuildInNameToPattern(pattern, sname, mname, false)
m[key] = &handlerItem {
rtype : gROUTE_REGISTER_CONTROLLER,
ctype : t,
fname : mname,
faddr : nil,
@ -100,6 +103,7 @@ func (s *Server)BindControllerRest(pattern string, c Controller) error {
}
key := name + ":" + pattern
m[key] = &handlerItem {
rtype : gROUTE_REGISTER_CONTROLLER,
ctype : v.Elem().Type(),
fname : name,
faddr : nil,

View File

@ -16,7 +16,8 @@ import (
// 注意该方法是直接绑定函数的内存地址,执行的时候直接执行该方法,不会存在初始化新的控制器逻辑
func (s *Server) BindHandler(pattern string, handler HandlerFunc) error {
return s.bindHandlerItem(pattern, &handlerItem{
return s.bindHandlerItem(pattern, &handlerItem {
rtype : gROUTE_REGISTER_HANDLER,
ctype : nil,
fname : "",
faddr : handler,

View File

@ -45,6 +45,7 @@ func (s *Server)BindObject(pattern string, obj interface{}, methods...string) er
}
key := s.mergeBuildInNameToPattern(pattern, sname, mname, true)
m[key] = &handlerItem {
rtype : gROUTE_REGISTER_OBJECT,
ctype : nil,
fname : "",
faddr : v.Method(i).Interface().(func(*Request)),
@ -61,6 +62,7 @@ func (s *Server)BindObject(pattern string, obj interface{}, methods...string) er
}
}
m[p] = &handlerItem {
rtype : gROUTE_REGISTER_OBJECT,
ctype : nil,
fname : "",
faddr : v.Method(i).Interface().(func(*Request)),
@ -94,6 +96,7 @@ func (s *Server)BindObjectMethod(pattern string, obj interface{}, method string)
}
key := s.mergeBuildInNameToPattern(pattern, sname, mname, false)
m[key] = &handlerItem{
rtype : gROUTE_REGISTER_OBJECT,
ctype : nil,
fname : "",
faddr : fval.Interface().(func(*Request)),
@ -126,6 +129,7 @@ func (s *Server)BindObjectRest(pattern string, obj interface{}) error {
}
key := name + ":" + pattern
m[key] = &handlerItem {
rtype : gROUTE_REGISTER_OBJECT,
ctype : nil,
fname : "",
faddr : v.Method(i).Interface().(func(*Request)),

View File

@ -260,10 +260,7 @@ func (c *Config) Close() {
func (c *Config) addMonitor(path string) {
if c.jsons.Get(path) == nil {
gfsnotify.Add(path, func(event *gfsnotify.Event) {
if event.IsRemove() {
gfsnotify.Remove(event.Path)
return
}
// 删除文件内容缓存,下一次查询会自动更新
c.jsons.Remove(event.Path)
})
}

View File

@ -287,8 +287,9 @@ func Chmod(path string, mode os.FileMode) error {
return os.Chmod(path, mode)
}
// 打开目录,并返回其下一级子目录名称列表,按照文件名称大小写进行排序
func ScanDir(path string) []string {
// 打开目录,并返回其下一级子目录名称列表,按照文件名称大小写进行排序,支持目录递归遍历。
// 当递归遍历时,结果集返回的是子级文件/目录的绝对路径,而不仅仅是一个名字
func ScanDir(path string, recursive ... bool) []string {
f, err := os.Open(path)
if err != nil {
return nil
@ -299,6 +300,17 @@ func ScanDir(path string) []string {
if err != nil {
return nil
}
// 是否递归遍历
if len(recursive) > 0 && recursive[0] && len(list) > 0 {
for k, v := range list {
p := fmt.Sprintf("%s%s%s", path, Separator, v)
list[k] = p
if IsDir(p) {
list = append(list, ScanDir(p, true)...)
}
}
}
// 默认按照字符串大小排序
sort.Slice(list, func(i, j int) bool { return list[i] < list[j] })
return list
}

View File

@ -44,26 +44,10 @@ const (
)
// 全局监听对象,方便应用端调用
var watcher, _ = New()
// 添加对指定文件/目录的监听,并给定回调函数
func Add(path string, callback func(event *Event)) error {
if watcher == nil {
return errors.New("global watcher creating failed")
}
return watcher.Add(path, callback)
}
// 移除监听
func Remove(path string) error {
if watcher == nil {
return errors.New("global watcher creating failed")
}
return watcher.Remove(path)
}
var watcher, _ = newWatcher()
// 创建监听管理对象
func New() (*Watcher, error) {
func newWatcher() (*Watcher, error) {
if watch, err := fsnotify.NewWatcher(); err == nil {
w := &Watcher {
watcher : watch,
@ -79,6 +63,22 @@ func New() (*Watcher, error) {
}
}
// 添加对指定文件/目录的监听,并给定回调函数;如果给定的是一个目录,默认递归监控。
func Add(path string, callback func(event *Event)) error {
if watcher == nil {
return errors.New("global watcher creating failed")
}
return watcher.AddRecursive(path, callback)
}
// 移除监听,默认递归删除。
func Remove(path string) error {
if watcher == nil {
return errors.New("global watcher creating failed")
}
return watcher.RemoveRecursive(path)
}
// 关闭监听管理对象
func (w *Watcher) Close() {
w.watcher.Close()
@ -110,12 +110,45 @@ func (w *Watcher) Add(path string, callback func(event *Event)) error {
return nil
}
// 递归添加监控
func (w *Watcher) AddRecursive(path string, callback func(event *Event)) error {
if gfile.IsDir(path) {
list := []string{path}
list = append(list, gfile.ScanDir(path, true)...)
for _, v := range list {
if err := w.Add(v, callback); err != nil {
return err
}
}
return nil
} else {
return w.Add(path, callback)
}
}
// 移除监听
func (w *Watcher) Remove(path string) error {
w.callbacks.Remove(path)
return w.watcher.Remove(path)
}
// 递归移除监听
func (w *Watcher) RemoveRecursive(path string) error {
if gfile.IsDir(path) {
list := []string{path}
list = append(list, gfile.ScanDir(path, true)...)
for _, v := range list {
if err := w.Remove(v); err != nil {
return err
}
}
return nil
} else {
return w.Remove(path)
}
}
// 监听循环
func (w *Watcher) startWatchLoop() {
go func() {
@ -139,23 +172,48 @@ func (w *Watcher) startWatchLoop() {
}()
}
// 检索给定path的回调方法**列表**
func (w *Watcher) getCallbacks(path string) *glist.List {
for path != "/" {
if l := w.callbacks.Get(path); l != nil {
return l.(*glist.List)
} else {
path = gfile.Dir(path)
}
}
return nil
}
// 事件循环
func (w *Watcher) startEventLoop() {
go func() {
for {
if v := w.events.PopFront(); v != nil {
event := v.(*Event)
// 如果是文件删除事件,判断该文件是否存在,如果存在,那么将此事件认为“假删除”,并重新添加监控
if event.IsRemove() && gfile.Exists(event.Path){
w.watcher.Add(event.Path)
continue
if event.IsRemove() {
if gfile.Exists(event.Path) {
// 如果是文件删除事件,判断该文件是否存在,如果存在,那么将此事件认为“假删除”,
// 并重新添加监控(底层fsnotify会自动删除掉监控这里重新添加回去)
w.watcher.Add(event.Path)
continue
} else {
// 如果是真实删除,那么递归删除监控信息
w.RemoveRecursive(event.Path)
}
}
if l := w.callbacks.Get(event.Path); l != nil {
go func(list interface{}) {
for _, v := range list.(*glist.List).FrontAll() {
v.(func(event *Event))(event)
callbacks := w.getCallbacks(event.Path)
// 如果创建了新的目录,那么将这个目录递归添加到监控中
if event.IsCreate() && gfile.IsDir(event.Path) {
for _, callback := range callbacks.FrontAll() {
w.AddRecursive(event.Path, callback.(func(event *Event)))
}
}
if callbacks != nil {
go func(callbacks *glist.List) {
for _, callback := range callbacks.FrontAll() {
callback.(func(event *Event))(event)
}
}(l)
}(callbacks)
}
} else {
break

View File

@ -6,22 +6,27 @@
package gfsnotify
// 文件/目录创建
func (e *Event) IsCreate() bool {
return e.Op & CREATE == CREATE
}
// 文件/目录修改
func (e *Event) IsWrite() bool {
return e.Op & WRITE == WRITE
}
// 文件/目录删除
func (e *Event) IsRemove() bool {
return e.Op & REMOVE == REMOVE
}
// 文件/目录重命名
func (e *Event) IsRename() bool {
return e.Op & RENAME == RENAME
}
// 文件/目录修改权限
func (e *Event) IsChmod() bool {
return e.Op & CHMOD == CHMOD
}

View File

@ -43,9 +43,9 @@ func (l *Logger) SetBacktraceSkip(skip int) {
// 可自定义IO接口
func (l *Logger) SetIO(w io.Writer) {
l.mu.RLock()
l.mu.Lock()
l.io = w
l.mu.RUnlock()
l.mu.Unlock()
}
// 返回自定义IO

View File

@ -159,10 +159,7 @@ func (view *View) funcInclude(file string, data...map[string]interface{}) templa
func (view *View) addMonitor(path string) {
if view.contents.Get(path) == "" {
gfsnotify.Add(path, func(event *gfsnotify.Event) {
if event.IsRemove() {
gfsnotify.Remove(event.Path)
return
}
// 删除文件内容缓存,下一次查询会自动更新
view.contents.Remove(event.Path)
})
}

View File

@ -0,0 +1,19 @@
// 路由重复注册检查 - handler
package main
import (
"gitee.com/johng/gf/g"
"gitee.com/johng/gf/g/net/ghttp"
)
func main() {
s := g.Server()
s.BindHandler("/", func(r *ghttp.Request){
r.Response.Writeln("哈喽世界!")
})
s.BindHandler("/", func(r *ghttp.Request){
r.Response.Writeln("哈喽世界!")
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,33 @@
// 路由重复注册检查 - controller
package main
import (
"gitee.com/johng/gf/g"
"gitee.com/johng/gf/g/frame/gmvc"
)
type User struct {
gmvc.Controller
}
func (u *User) Index() {
u.Response.Write("User")
}
func (u *User) Info() {
u.Response.Write("Info - Uid: ", u.Request.Get("uid"))
}
func (u *User) List() {
u.Response.Write("List - Page: ", u.Request.Get("page"))
}
func main() {
s := g.Server()
s.BindController("/user", new(User))
s.BindController("/user/{.method}/{uid}", new(User), "Info")
s.BindController("/user/{.method}/{page}.html", new(User), "List")
s.BindController("/user/{.method}/{page}.html", new(User), "List")
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,25 @@
// 路由重复注册检查 - object
package main
import (
"gitee.com/johng/gf/g"
"gitee.com/johng/gf/g/net/ghttp"
)
type Object struct {}
func (o *Object) Index(r *ghttp.Request) {
r.Response.Write("object index")
}
func (o *Object) Show(r *ghttp.Request) {
r.Response.Write("object show")
}
func main() {
s := g.Server()
g.Server().BindObject("/object", new(Object))
g.Server().BindObject("/object", new(Object))
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,11 @@
package main
import (
"gitee.com/johng/gf/g/util/gutil"
"gitee.com/johng/gf/g/os/gfile"
)
func main() {
gutil.Dump(gfile.ScanDir("/home/john/Documents"))
gutil.Dump(gfile.ScanDir("/home/john/temp/newproject", true))
}

View File

@ -13,7 +13,7 @@ func main() {
}
defer watch.Close()
//添加要监控的对象,文件或文件夹
err = watch.Add("/home/john/Documents/temp")
err = watch.Add("/home/john/temp")
if err != nil {
log.Fatal(err)
}

View File

@ -6,7 +6,7 @@ import (
)
func main() {
err := gfsnotify.Add("./temp.txt", func(event *gfsnotify.Event) {
err := gfsnotify.Add("/home/john/temp", func(event *gfsnotify.Event) {
if event.IsCreate() {
log.Println("创建文件 : ", event.Path)
}