初步完成第一阶段的性能改进

This commit is contained in:
John 2018-11-17 02:39:23 +08:00
parent 778595dd8f
commit b190ee9fe6
22 changed files with 295 additions and 315 deletions

2
.gitignore vendored
View File

@ -13,6 +13,6 @@ gitpush.sh
pkg/
bin/
cbuild
*/.DS_Store
**/.DS_Store
.vscode/
go.sum

View File

@ -159,7 +159,7 @@ func (r *Response) ServeFile(path string) {
r.request.isFileServe = true
// 首先判断是否给定的path已经是一个绝对路径
if !gfile.Exists(path) {
path = r.Server.paths.Search(path)
path, _ = r.Server.paths.Search(path)
}
if path == "" {
r.WriteStatus(http.StatusNotFound)

View File

@ -237,6 +237,18 @@ func (s *Server) Start() error {
}
}
// 配置相关相对路径处理
if !gfile.Exists(s.config.HTTPSCertPath) {
if t, _ := s.paths.Search(s.config.HTTPSCertPath); t != "" {
s.config.HTTPSCertPath = t
}
}
if !gfile.Exists(s.config.HTTPSKeyPath) {
if t, _ := s.paths.Search(s.config.HTTPSKeyPath); t != "" {
s.config.HTTPSKeyPath = t
}
}
// gzip压缩文件类型
//if s.config.GzipContentTypes != nil {
// for _, v := range s.config.GzipContentTypes {

View File

@ -11,7 +11,6 @@ import (
"fmt"
"gitee.com/johng/gf/g/encoding/ghtml"
"gitee.com/johng/gf/g/os/gfile"
"gitee.com/johng/gf/g/os/glog"
"gitee.com/johng/gf/g/os/gtime"
"net/http"
"net/url"
@ -55,12 +54,28 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) {
s.callHookHandler(HOOK_AFTER_CLOSE, request)
}()
// ============================================================
// 优先级控制:
// 静态文件 > 动态服务 > 静态目录
// ============================================================
// 优先执行静态文件检索(检测是否存在对应的静态文件包括index files处理)
staticFile := s.paths.Search(r.URL.Path, s.config.IndexFiles...)
staticFile, isStaticDir := s.paths.Search(r.URL.Path, s.config.IndexFiles...)
if staticFile != "" {
request.isFileRequest = true
}
glog.Info(staticFile)
// 动态服务检索
handler := (*handlerItem)(nil)
if !request.IsFileRequest() || isStaticDir {
if parsedItem := s.getServeHandlerWithCache(request); parsedItem != nil {
handler = parsedItem.handler
for k, v := range parsedItem.values {
request.routerVars[k] = v
}
request.Router = parsedItem.handler.router
}
}
// 事件 - BeforeServe
s.callHookHandler(HOOK_BEFORE_SERVE, request)
@ -68,22 +83,20 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) {
// 执行静态文件服务/回调控制器/执行对象/方法
if !request.IsExited() {
// 需要再次判断文件是否真实存在,因为文件检索可能使用了缓存,从健壮性考虑这里需要二次判断
if request.IsFileRequest() && gfile.Exists(staticFile) && gfile.SelfPath() != staticFile {
if request.IsFileRequest() && !isStaticDir /* && gfile.Exists(staticFile) */ && gfile.SelfPath() != staticFile {
// 静态文件
s.serveFile(request, staticFile)
} else {
// 动态服务检索
handler := (*handlerItem)(nil)
if parsedItem := s.getServeHandlerWithCache(request); parsedItem != nil {
handler = parsedItem.handler
for k, v := range parsedItem.values {
request.routerVars[k] = v
}
request.Router = parsedItem.handler.router
}
if handler != nil {
// 动态服务
s.callServeHandler(handler, request)
} else {
request.Response.WriteStatus(http.StatusNotFound)
if isStaticDir {
// 静态目录
s.serveFile(request, staticFile)
} else {
request.Response.WriteStatus(http.StatusNotFound)
}
}
}
}

View File

@ -71,8 +71,8 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ... strin
}
regkey := s.hookHandlerKey(hookName, method, uri, domain)
caller := s.getHandlerRegisterCallerLine(handler)
if line, ok := s.routesMap[regkey]; ok {
s := fmt.Sprintf(`duplicated route registry "%s" in %s , former in %s`, pattern, caller, line)
if item, ok := s.routesMap[regkey]; ok {
s := fmt.Sprintf(`duplicated route registry "%s", already registered in %s`, pattern, item.file)
glog.Errorfln(s)
return errors.New(s)
} else {

View File

@ -10,7 +10,8 @@ import "gitee.com/johng/gf/g/os/gtime"
// 判断缓存项是否已过期
func (item *memCacheItem) IsExpired() bool {
if item.e > gtime.Millisecond() {
// 注意这里应当包含等于试想一下缓存时间只有最小粒度为1毫秒的情况
if item.e >= gtime.Millisecond() {
return false
}
return true

View File

@ -53,7 +53,8 @@ func (c *Config) filePath(file...string) string {
if len(file) > 0 {
name = file[0]
}
return c.paths.Search(name)
path, _ := c.paths.Search(name)
return path
}
// 设置配置管理器的配置文件存放目录绝对路径
@ -92,7 +93,8 @@ func (c *Config) GetFilePath(file...string) string {
if len(file) > 0 {
name = file[0]
}
return c.paths.Search(name)
path, _ := c.paths.Search(name)
return path
}
// 设置配置管理对象的默认文件名称

View File

@ -255,15 +255,15 @@ func ScanDir(path string, pattern string, recursive ... bool) ([]string, error)
// 内部检索目录方法,支持递归,返回没有排序的文件绝对路径列表结果。
// pattern参数支持多个文件名称模式匹配使用','符号分隔多个模式。
func doScanDir(path string, pattern string, recursive ... bool) ([]string, error) {
var list []string
list := ([]string)(nil)
// 打开目录
dfile, err := os.Open(path)
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer dfile.Close()
defer file.Close()
// 读取目录下的文件列表
names, err := dfile.Readdirnames(-1)
names, err := file.Readdirnames(-1)
if err != nil {
return nil, err
}

View File

@ -11,36 +11,29 @@ import (
"container/list"
"errors"
"fmt"
"gitee.com/johng/gf/g/container/glist"
"gitee.com/johng/gf/g/container/gmap"
"gitee.com/johng/gf/g/container/gqueue"
"gitee.com/johng/gf/g/container/gtype"
"gitee.com/johng/gf/g/os/gcache"
"gitee.com/johng/gf/g/os/gcmd"
"gitee.com/johng/gf/g/os/genv"
"gitee.com/johng/gf/g/util/gconv"
"gitee.com/johng/gf/third/github.com/fsnotify/fsnotify"
)
// 监听管理对象
type Watcher struct {
watchers []*fsnotify.Watcher // 底层fsnotify对象支持多个以避免单个inotify对象监听队列上限问题
watcher *fsnotify.Watcher // 底层fsnotify对象
events *gqueue.Queue // 过滤后的事件通知,不会出现重复事件
cache *gcache.Cache // 缓存对象,主要用于事件重复过滤
callbacks *gmap.StringInterfaceMap // 注册的所有绝对路径机器对应的回调函数列表map
recursivePaths *gmap.StringInterfaceMap // 支持递归监听的目录绝对路径及其对应的回调函数列表map
callbacks *gmap.StringInterfaceMap // 注册的所有绝对路径(文件/目录)及其对应的回调函数列表map
closeChan chan struct{} // 关闭事件
}
// 注册的监听回调方法
type Callback struct {
Id int // 唯一ID
Func func(event *Event) // 回调方法
Path string // 监听的文件/目录
addr string // Func对应的内存地址用以判断回调的重复
elem *list.Element // 指向监听链表中的元素项位置
parent *Callback // 父级callback有这个属性表示该callback为被自动管理的callback
subs *glist.List // 子级回调对象指针列表
Id int // 唯一ID
Func func(event *Event) // 回调方法
Path string // 监听的文件/目录
elem *list.Element // 指向回调函数链表中的元素项位置(便于删除)
recursive bool // 当目录时,是否递归监听(使用在子文件/目录回溯查找回调函数时)
}
// 监听事件对象
@ -65,77 +58,45 @@ const (
const (
REPEAT_EVENT_FILTER_INTERVAL = 1 // (毫秒)重复事件过滤间隔
DEFAULT_WATCHER_COUNT = 1 // 默认创建的监控对象数量(使用哈希取模)
gDEFAULT_PKG_WATCHER_COUNT = 4 // 默认创建的包监控对象数量(使用哈希取模)
)
var (
// 默认的Watcher对象
defaultWatcher *Watcher
defaultWatcher, _ = New()
// 默认的watchers是否初始化使用时才创建
watcherInited = gtype.NewBool()
watcherInited = gtype.NewBool()
// 回调方法ID与对象指针的映射哈希表用于根据ID快速查找回调对象
callbackIdMap = gmap.NewIntInterfaceMap()
callbackIdMap = gmap.NewIntInterfaceMap()
// 回调函数的ID生成器(原子操作)
callbackIdGenerator = gtype.NewInt()
)
// 初始化创建watcher对象用于包默认管理监听
func initWatcher() {
if !watcherInited.Set(true) {
pkgWatcherCount := gconv.Int(genv.Get("GF_INOTIFY_COUNT"))
if pkgWatcherCount == 0 {
pkgWatcherCount = gconv.Int(gcmd.Option.Get("gf.inotify-count"))
}
if pkgWatcherCount == 0 {
pkgWatcherCount = gDEFAULT_PKG_WATCHER_COUNT
}
if w, err := New(pkgWatcherCount); err == nil {
defaultWatcher = w
} else {
panic(err)
}
}
}
// 创建监听管理对象主要注意的是创建监听对象会占用系统的inotify句柄数量受到 fs.inotify.max_user_instances 的限制
func New(inotifyCount...int) (*Watcher, error) {
count := DEFAULT_WATCHER_COUNT
if len(inotifyCount) > 0 {
count = inotifyCount[0]
}
func New() (*Watcher, error) {
w := &Watcher {
cache : gcache.New(),
watchers : make([]*fsnotify.Watcher, count),
events : gqueue.New(),
closeChan : make(chan struct{}),
callbacks : gmap.NewStringInterfaceMap(),
recursivePaths : gmap.NewStringInterfaceMap(),
cache : gcache.New(),
events : gqueue.New(),
closeChan : make(chan struct{}),
callbacks : gmap.NewStringInterfaceMap(),
}
for i := 0; i < count; i++ {
if watcher, err := fsnotify.NewWatcher(); err == nil {
w.watchers[i] = watcher
} else {
// 出错关闭已创建的底层watcher对象
for j := 0; j < i; j++ {
w.watchers[j].Close()
}
return nil, err
}
if watcher, err := fsnotify.NewWatcher(); err == nil {
w.watcher = watcher
} else {
return nil, err
}
w.startWatchLoop()
w.startEventLoop()
return w, nil
}
// 添加对指定文件/目录的监听,并给定回调函数;如果给定的是一个目录,默认递归监控。
// 添加对指定文件/目录的监听,并给定回调函数;如果给定的是一个目录,默认递归监控。
func Add(path string, callbackFunc func(event *Event), recursive...bool) (callback *Callback, err error) {
return watcher().Add(path, callbackFunc, recursive...)
return defaultWatcher.Add(path, callbackFunc, recursive...)
}
// 递归移除对指定文件/目录的所有监听回调
func Remove(path string) error {
return watcher().Remove(path)
return defaultWatcher.Remove(path)
}
// 根据指定的回调函数ID移出指定的inotify回调函数
@ -147,11 +108,6 @@ func RemoveCallback(callbackId int) error {
if callback == nil {
return errors.New(fmt.Sprintf(`callback for id %d not found`, callbackId))
}
return watcher().RemoveCallback(callbackId)
}
// 获得默认的包watcher
func watcher() *Watcher {
initWatcher()
return defaultWatcher
defaultWatcher.RemoveCallback(callbackId)
return nil
}

View File

@ -49,9 +49,35 @@ func fileIsDir(path string) bool {
return s.IsDir()
}
// 返回制定目录其子级所有的目录绝对路径(包含自身)
func fileAllDirs(path string) (list []string) {
list = []string{path}
// 打开目录
file, err := os.Open(path)
if err != nil {
return list
}
defer file.Close()
// 读取目录下的文件列表
names, err := file.Readdirnames(-1)
if err != nil {
return list
}
// 是否递归遍历
for _, name := range names {
path := fmt.Sprintf("%s%s%s", path, string(filepath.Separator), name)
if fileIsDir(path) {
if array := fileAllDirs(path); len(array) > 0 {
list = append(list, array...)
}
}
}
return
}
// 打开目录,并返回其下一级文件列表(绝对路径),按照文件名称大小写进行排序,支持目录递归遍历。
func fileScanDir(path string, pattern string, recursive ... bool) ([]string, error) {
list, err := fileDoScanDir(path, pattern, recursive...)
list, err := doFileScanDir(path, pattern, recursive...)
if err != nil {
return nil, err
}
@ -63,16 +89,16 @@ func fileScanDir(path string, pattern string, recursive ... bool) ([]string, err
// 内部检索目录方法,支持递归,返回没有排序的文件绝对路径列表结果。
// pattern参数支持多个文件名称模式匹配使用','符号分隔多个模式。
func fileDoScanDir(path string, pattern string, recursive ... bool) ([]string, error) {
var list []string
func doFileScanDir(path string, pattern string, recursive ... bool) ([]string, error) {
list := ([]string)(nil)
// 打开目录
dfile, err := os.Open(path)
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer dfile.Close()
defer file.Close()
// 读取目录下的文件列表
names, err := dfile.Readdirnames(-1)
names, err := file.Readdirnames(-1)
if err != nil {
return nil, err
}
@ -80,7 +106,7 @@ func fileDoScanDir(path string, pattern string, recursive ... bool) ([]string, e
for _, name := range names {
path := fmt.Sprintf("%s%s%s", path, string(filepath.Separator), name)
if fileIsDir(path) && len(recursive) > 0 && recursive[0] {
array, _ := fileDoScanDir(path, pattern, true)
array, _ := doFileScanDir(path, pattern, true)
if len(array) > 0 {
list = append(list, array...)
}
@ -93,4 +119,4 @@ func fileDoScanDir(path string, pattern string, recursive ... bool) ([]string, e
}
}
return list, nil
}
}

View File

@ -10,74 +10,45 @@ import (
"errors"
"fmt"
"gitee.com/johng/gf/g/container/glist"
"gitee.com/johng/gf/g/encoding/ghash"
"gitee.com/johng/gf/third/github.com/fsnotify/fsnotify"
)
// 添加监控path参数支持文件或者目录路径recursive为非必需参数默认为递归添加监控(当path为目录时)。
// 添加监控path参数支持文件或者目录路径recursive为非必需参数默认为递归监控(当path为目录时)。
// 如果添加目录这里只会返回目录的callback按照callback删除时会递归删除。
func (w *Watcher) Add(path string, callbackFunc func(event *Event), recursive...bool) (callback *Callback, err error) {
return w.addWithCallbackFunc(nil, path, callbackFunc, recursive...)
}
// 添加监控path参数支持文件或者目录路径recursive为非必需参数默认为递归添加监控(当path为目录时)。
// 如果添加目录这里只会返回目录的callback按照callback删除时会递归删除。
func (w *Watcher) addWithCallbackFunc(parentCallback *Callback, path string, callbackFunc func(event *Event), recursive...bool) (callback *Callback, err error) {
// 首先添加这个文件/目录
callback, err = w.doAddWithCallbackFunc(path, callbackFunc, parentCallback)
callback, err = w.addWithCallbackFunc(path, callbackFunc, recursive...)
if err != nil {
return nil, err
}
// 其次递归添加其下的文件/目录
// 如果需要递归,那么递归添加其下的子级目录,
// 注意!!
// 1、这里只递归添加**目录**, 而非文件,因为监控了目录即监控了其下一级的文件;
// 2、这里只是添加底层监控对象对**子级所有目录**的监控,没有任何回调函数的设置,在事件产生时会回溯查找父级的回调函数;
if fileIsDir(path) && (len(recursive) == 0 || recursive[0]) {
// 追加递归监控的回调到recursivePaths中
w.recursivePaths.LockFunc(func(m map[string]interface{}) {
list := (*glist.List)(nil)
if v, ok := m[path]; !ok {
list = glist.New()
m[path] = list
} else {
list = v.(*glist.List)
for _, subPath := range fileAllDirs(path) {
if fileIsDir(subPath) {
w.watcher.Add(subPath)
}
list.PushBack(callback)
})
// 递归添加监控
paths, _ := fileScanDir(path, "*", true)
for _, v := range paths {
w.doAddWithCallbackFunc(v, callbackFunc, callback)
}
}
return
}
// 添加对指定文件/目录的监听,并给定回调函数
func (w *Watcher) doAddWithCallbackFunc(path string, callbackFunc func(event *Event), parentCallback *Callback) (callback *Callback, err error) {
func (w *Watcher) addWithCallbackFunc(path string, callbackFunc func(event *Event), recursive...bool) (callback *Callback, err error) {
// 这里统一转换为当前系统的绝对路径,便于统一监控文件名称
if t := fileRealPath(path); t == "" {
return nil, errors.New(fmt.Sprintf(`"%s" does not exist`, path))
} else {
path = t
}
// 添加成功后会注册该callback id到全局的哈希表并绑定到父级的注册回调中
defer func() {
if err == nil {
if parentCallback == nil {
// 只有主callback才记录到id map中因为子callback是自动管理的无需添加到全局id映射map中
callbackIdMap.Set(callback.Id, callback)
}
if parentCallback != nil {
// 添加到直属父级的subs属性中建立关联关系便于后续删除
parentCallback.subs.PushBack(callback)
}
}
}()
callback = &Callback {
Id : callbackIdGenerator.Add(1),
Func : callbackFunc,
Path : path,
addr : fmt.Sprintf("%p", callbackFunc)[2:],
subs : glist.New(),
parent : parentCallback,
}
if len(recursive) > 0 {
callback.recursive = recursive[0]
}
// 注册回调函数
w.callbacks.LockFunc(func(m map[string]interface{}) {
@ -91,93 +62,80 @@ func (w *Watcher) doAddWithCallbackFunc(path string, callbackFunc func(event *Ev
callback.elem = list.PushBack(callback)
})
// 添加底层监听
w.watcher(path).Add(path)
w.watcher.Add(path)
// 添加成功后会注册该callback id到全局的哈希表
callbackIdMap.Set(callback.Id, callback)
return
}
// 根据path查询对应的底层watcher对象
func (w *Watcher) watcher(path string) *fsnotify.Watcher {
return w.watchers[ghash.BKDRHash([]byte(path)) % uint32(len(w.watchers))]
}
// 关闭监听管理对象
func (w *Watcher) Close() {
for _, watcher := range w.watchers {
watcher.Close()
}
w.events.Close()
w.watcher.Close()
close(w.closeChan)
}
// 递归移除对指定文件/目录的所有监听回调
func (w *Watcher) Remove(path string) error {
if fileIsDir(path) && fileExists(path) {
paths, _ := fileScanDir(path, "*", true)
paths = append(paths, path)
for _, v := range paths {
if err := w.removeWatch(v); err != nil {
return err
}
}
return nil
} else {
return w.removeWatch(path)
}
}
// 移除对指定文件/目录的所有监听
func (w *Watcher) removeWatch(path string) error {
// 首先移除所有该path的回调注册
if r := w.callbacks.Get(path); r != nil {
// 首先移除path注册的回调注册以及callbackIdMap中的ID
if r := w.callbacks.Remove(path); r != nil {
list := r.(*glist.List)
for {
if r := list.PopFront(); r != nil {
w.removeCallback(r.(*Callback))
callbackIdMap.Remove(r.(*Callback).Id)
} else {
break
}
}
}
// 其次移除该path的监听注册
w.callbacks.Remove(path)
// 其次递归判断所有的子级是否可删除监听
if subPaths, err := fileScanDir(path, "*", true); err == nil && len(subPaths) > 0 {
for _, subPath := range subPaths {
if w.checkPathCanBeRemoved(subPath) {
w.watcher.Remove(subPath)
}
}
}
// 最后移除底层的监听
return w.watcher(path).Remove(path)
return w.watcher.Remove(path)
}
// 判断给定的路径是否可以删除监听(只有所有回调函数都没有了才能删除)
func (w *Watcher) checkPathCanBeRemoved(path string) bool {
// 首先检索path对应的回调函数
if v := w.callbacks.Get(path); v != nil {
return false
}
// 其次查找父级目录有无回调注册
dirPath := fileDir(path)
if v := w.callbacks.Get(dirPath); v != nil {
return false
}
// 最后回溯查找递归回调函数
for {
parentDirPath := fileDir(dirPath)
if parentDirPath == dirPath {
break
}
if v := w.callbacks.Get(parentDirPath); v != nil {
return false
}
dirPath = parentDirPath
}
return true
}
// 根据指定的回调函数ID移出指定的inotify回调函数
func (w *Watcher) RemoveCallback(callbackId int) error {
func (w *Watcher) RemoveCallback(callbackId int) {
callback := (*Callback)(nil)
if r := callbackIdMap.Get(callbackId); r != nil {
callback = r.(*Callback)
}
if callback == nil {
return errors.New(fmt.Sprintf(`callback for id %d not found`, callbackId))
if callback != nil {
if r := w.callbacks.Get(callback.Path); r != nil {
r.(*glist.List).Remove(callback.elem)
}
callbackIdMap.Remove(callbackId)
}
w.removeCallback(callback)
return nil
}
// (递归)移除对指定文件/目录的所有监听
func (w *Watcher) removeCallback(callback *Callback) error {
if r := w.callbacks.Get(callback.Path); r != nil {
list := r.(*glist.List)
list.Remove(callback.elem)
// 如果存在子级callback那么也一并递归删除
if callback.subs.Len() > 0 {
for {
if r := callback.subs.PopFront(); r != nil {
w.removeCallback(r.(*Callback))
} else {
break
}
}
}
// 如果该文件/目录的所有回调都被删除,那么移除底层的监听
if list.Len() == 0 {
return w.watcher(callback.Path).Remove(callback.Path)
}
} else {
return errors.New(fmt.Sprintf(`callbacks not found for "%s"`, callback.Path))
}
return nil
}

View File

@ -9,67 +9,70 @@ package gfsnotify
import (
"fmt"
"gitee.com/johng/gf/g/container/glist"
"sync"
"time"
)
// 监听循环
func (w *Watcher) startWatchLoop() {
for i := 0; i < len(w.watchers); i++ {
go func(i int) {
for {
select {
// 关闭事件
case <- w.closeChan: return
go func() {
for {
select {
// 关闭事件
case <- w.closeChan: return
// 监听事件
case ev := <- w.watchers[i].Events:
//fmt.Println("ev:", ev.String())
w.cache.SetIfNotExist(ev.String(), func() interface{} {
w.events.Push(&Event{
event : ev,
Path : ev.Name,
Op : Op(ev.Op),
Watcher : w,
})
return struct {}{}
}, REPEAT_EVENT_FILTER_INTERVAL)
// 监听事件
case ev := <- w.watcher.Events:
//fmt.Println("ev:", ev.String())
w.cache.SetIfNotExist(ev.String(), func() interface{} {
w.events.Push(&Event{
event : ev,
Path : ev.Name,
Op : Op(ev.Op),
Watcher : w,
})
return struct {}{}
}, REPEAT_EVENT_FILTER_INTERVAL)
case err := <- w.watchers[i].Errors:
fmt.Errorf("error: %s\n" + err.Error());
}
case err := <- w.watcher.Errors:
fmt.Errorf("error: %s\n" + err.Error());
}
}(i)
}
}
}()
}
// 获得真正监听的文件路径及回调函数列表,假如是临时文件或者新增文件,是无法搜索都监听回调的。
// 判断规则:
// 1、在 callbacks 中应当有回调注册函数(否则监听根本没意义)
// 2、如果该path下不存在回调注册函数则按照path长度从右往左递减直到减到目录地址为止(不包含)
// 3、如果仍旧无法匹配回调函数那么忽略否则使用查找到的新path覆盖掉event的path
// 解决问题:
// 1、部分IDE修改文件时生成的临时文件如: /index.html -> /index.html__jdold__
func (w *Watcher) getWatchPathAndCallbacks(path string) (watchPath string, callbacks *glist.List) {
if path == "" {
return "", nil
// 获得文件路径的监听回调,包括层级的监听回调。
func (w *Watcher) getCallbacks(path string) (callbacks []*Callback) {
// 首先检索path对应的回调函数
if v := w.callbacks.Get(path); v != nil {
for _, v := range v.(*glist.List).FrontAll() {
callback := v.(*Callback)
callbacks = append(callbacks, callback)
}
}
// 其次查找父级目录有无回调注册
dirPath := fileDir(path)
for {
if v := w.callbacks.Get(path); v != nil {
return path, v.(*glist.List)
}
path = path[0 : len(path) - 1]
// 递减到上一级目录为止
if path == dirPath {
break
}
// 如果不能再继续递减,那么退出
if len(path) == 0 {
break
if v := w.callbacks.Get(dirPath); v != nil {
for _, v := range v.(*glist.List).FrontAll() {
callback := v.(*Callback)
callbacks = append(callbacks, callback)
}
}
return "", nil
// 最后回溯查找递归回调函数
for {
parentDirPath := fileDir(dirPath)
if parentDirPath == dirPath {
break
}
if v := w.callbacks.Get(parentDirPath); v != nil {
for _, v := range v.(*glist.List).FrontAll() {
callback := v.(*Callback)
if callback.recursive {
callbacks = append(callbacks, callback)
}
}
}
dirPath = parentDirPath
}
return
}
// 事件循环
@ -78,63 +81,54 @@ func (w *Watcher) startEventLoop() {
for {
if v := w.events.Pop(); v != nil {
event := v.(*Event)
// watchPath是注册回调的路径可能和event.Path不一样
watchPath, callbacks := w.getWatchPathAndCallbacks(event.Path)
if callbacks == nil {
// 如果该路径一个回调也没有,那么没有必要执行后续逻辑,删除对该文件的监听
callbacks := w.getCallbacks(event.Path)
if len(callbacks) == 0 {
w.watcher.Remove(event.Path)
continue
}
fmt.Println("event:", event.String(), watchPath, fileExists(watchPath))
switch {
// 如果是删除操作,那么需要判断是否文件真正不存在了,如果存在,那么将此事件认为“假删除”
case event.IsRemove():
if fileExists(watchPath) {
// 重新添加监控(底层fsnotify会自动删除掉监控这里重新添加回去)
// 注意这里调用的是底层fsnotify添加监控只会产生回调事件并不会使回调函数重复注册
w.watcher(watchPath).Add(event.Path)
if fileExists(event.Path) {
// 底层重新添加监控(不用担心重复添加)
w.watcher.Add(event.Path)
// 修改事件操作为重命名(相当于重命名为自身名称,最终名称没变)
event.Op = RENAME
} else {
// 删除之前需要执行一遍回调否则Remove之后就无法执行了
// 由于是异步回调,这里保证所有回调都开始执行后再执行删除
wg := sync.WaitGroup{}
for _, v := range callbacks.FrontAll() {
wg.Add(1)
go func(callback *Callback) {
wg.Done()
callback.Func(event)
}(v.(*Callback))
}
wg.Wait()
time.Sleep(time.Second)
// 如果是真实删除,那么递归删除监控信息
fmt.Println("remove", watchPath)
w.Remove(watchPath)
}
// 如果是重命名操作,那么需要判断是否文件真正不存在了,如果存在,那么将此事件认为“假命名”
// (特别是某些编辑器在编辑文件时会先对文件RENAME再CHMOD)
case event.IsRename():
if fileExists(watchPath) {
// 重新添加监控
w.watcher(watchPath).Add(watchPath)
} else if watchPath != event.Path && fileExists(event.Path) {
for _, v := range callbacks.FrontAll() {
callback := v.(*Callback)
w.addWithCallbackFunc(callback, event.Path, callback.Func)
}
if fileExists(event.Path) {
// 底层有可能去掉了监控, 这里重新添加监控(不用担心重复添加)
w.watcher.Add(event.Path)
// 修改事件操作为修改属性
event.Op = CHMOD
}
// 创建文件/目录
case event.IsCreate():
for _, v := range callbacks.FrontAll() {
callback := v.(*Callback)
w.addWithCallbackFunc(callback, event.Path, callback.Func)
// =========================================
// 注意这里只是添加底层监听,并没有注册任何的回调函数,
// 默认的回调函数为父级的递归回调
// =========================================
if fileIsDir(event.Path) {
// 递归添加
for _, subPath := range fileAllDirs(event.Path) {
if fileIsDir(subPath) {
w.watcher.Add(subPath)
}
}
} else {
// 添加文件监听
w.watcher.Add(event.Path)
}
}
// 执行回调处理,异步处理
for _, v := range callbacks.FrontAll() {
go v.(*Callback).Func(event)
for _, callback := range callbacks {
go callback.Func(event)
}
} else {

View File

@ -15,9 +15,9 @@ import (
"gitee.com/johng/gf/g/container/gmap"
"gitee.com/johng/gf/g/os/gfile"
"gitee.com/johng/gf/g/os/gfsnotify"
"gitee.com/johng/gf/g/os/glog"
"gitee.com/johng/gf/g/util/gstr"
"runtime"
"sort"
"strings"
)
@ -33,7 +33,6 @@ type SPathCacheItem struct {
isDir bool // 是否目录
}
// 创建一个搜索对象
func New () *SPath {
return &SPath {
@ -46,7 +45,7 @@ func New () *SPath {
func (sp *SPath) Set(path string) (realPath string, err error) {
realPath = gfile.RealPath(path)
if realPath == "" {
realPath = sp.Search(path)
realPath, _ = sp.Search(path)
if realPath == "" {
realPath = gfile.RealPath(gfile.Pwd() + gfile.Separator + path)
}
@ -80,7 +79,7 @@ func (sp *SPath) Set(path string) (realPath string, err error) {
func (sp *SPath) Add(path string) (realPath string, err error) {
realPath = gfile.RealPath(path)
if realPath == "" {
realPath = sp.Search(path)
realPath, _ = sp.Search(path)
if realPath == "" {
realPath = gfile.RealPath(gfile.Pwd() + gfile.Separator + path)
}
@ -107,22 +106,32 @@ func (sp *SPath) Add(path string) (realPath string, err error) {
}
// 给定的name只是相对文件路径找不到该文件时返回空字符串;
// 当给定indexFiles时如果name时一个目录那么会进一步检索其下对应的indexFiles文件是否存在存在则返回indexFile 绝对路径;
// 当给定indexFiles时如果name时一个目录那么会进一步检索其下对应的indexFiles文件是否存在存在则返回indexFile绝对路径;
// 否则返回name目录绝对路径。
func (sp *SPath) Search(name string, indexFiles...string) string {
func (sp *SPath) Search(name string, indexFiles...string) (path string, isDir bool) {
name = sp.formatCacheName(name)
if v := sp.cache.Get(name); v != nil {
item := v.(*SPathCacheItem)
if len(indexFiles) > 0 && item.isDir {
for _, file := range indexFiles {
if v := sp.cache.Get(name + "/" + file); v != nil {
return v.(*SPathCacheItem).path
item := v.(*SPathCacheItem)
return item.path, item.isDir
}
}
}
return item.path
return item.path, item.isDir
}
return ""
return "", false
}
// 返回当前对象缓存的所有路径列表
func (sp *SPath) AllPaths() []string {
paths := sp.cache.Keys()
if len(paths) > 0 {
sort.Strings(paths)
}
return paths
}
// 当前的搜索路径数量
@ -140,6 +149,7 @@ func (sp *SPath) formatCacheName(name string) string {
if name == "" {
return "/"
}
name = strings.TrimLeft(name, ".")
if runtime.GOOS != "linux" {
name = gstr.Replace(name, "\\", "/")
}
@ -180,7 +190,7 @@ func (sp *SPath) addToCache(filePath, dirPath string) {
// 这里需要注意的点是,由于添加监听是递归添加的,那么假如删除一个目录,那么该目录下的文件(包括目录)也会产生一条删除事件总共会产生N条事件。
func (sp *SPath) addMonitorByPath(path string) {
gfsnotify.Add(path, func(event *gfsnotify.Event) {
glog.Debug(event.String())
//glog.Debug(event.String())
switch {
case event.IsRemove():
sp.cache.Remove(sp.nameFromPath(event.Path, path))
@ -193,7 +203,7 @@ func (sp *SPath) addMonitorByPath(path string) {
case event.IsCreate():
sp.addToCache(event.Path, path)
}
})
}, true)
}
// 删除监听(递归)

View File

@ -141,7 +141,7 @@ func (view *View) Assign(key string, value interface{}) {
// 解析模板,返回解析后的内容
func (view *View) Parse(file string, params Params, funcmap...map[string]interface{}) ([]byte, error) {
path := view.paths.Search(file)
path, _ := view.paths.Search(file)
if path == "" {
return nil, errors.New("tpl \"" + file + "\" not found")
}

View File

@ -10,7 +10,7 @@ func main() {
s.BindHandler("/", func(r *ghttp.Request){
r.Response.Writeln("您可以同时通过HTTP和HTTPS方式看到该内容")
})
s.EnableHTTPS("/home/john/temp/server.crt", "/home/john/temp/server.key")
s.EnableHTTPS("./server.crt", "./server.key")
s.SetHTTPSPort(8198, 8199)
s.SetPort(8200, 8300)
s.EnableAdmin()

View File

@ -6,7 +6,7 @@ import "gitee.com/johng/gf/g"
func main() {
s := g.Server()
s.SetIndexFolder(true)
s.SetServerRoot("/Users/john/Documents")
s.SetServerRoot("/Users/john/Temp")
s.SetPort(8199)
s.Run()
}

View File

@ -2,11 +2,11 @@ package main
import (
"fmt"
"gitee.com/johng/gf/g/os/gcfg"
"gitee.com/johng/gf/g"
)
func main() {
c := gcfg.New("/home/john/Workspace/Go/GOPATH/src/gitee.com/johng/gf/geg/os/gcfg")
c := g.Config()
redisConfig := c.GetArray("redis-cache", "redis.toml")
memConfig := c.GetArray("", "memcache.yml")
fmt.Println(redisConfig)

View File

@ -2,11 +2,11 @@ package main
import (
"fmt"
"gitee.com/johng/gf/g/os/gcfg"
"gitee.com/johng/gf/g"
)
func main() {
c := gcfg.New("/home/john/Workspace/Go/GOPATH/src/gitee.com/johng/gf/geg/os/gcfg")
c := g.Config()
fmt.Println(c.GetArray("memcache"))
}

View File

@ -15,7 +15,7 @@ func main() {
defer watch.Close()
//添加要监控的对象,文件或文件夹
//err = watch.Add("D:\\Workspace\\Go\\GOPATH\\src\\gitee.com\\johng\\gf\\geg\\other\\test.go")
err = watch.Add("/Users/john/Temp/1/2")
err = watch.Add("/Users/john/Workspace/Go/GOPATH/src/gitee.com/johng/gf/geg/other/test.go")
if err != nil {
log.Fatal(err)
}

View File

@ -7,10 +7,10 @@ import (
func main() {
//path := "D:\\Workspace\\Go\\GOPATH\\src\\gitee.com\\johng\\gf\\geg\\other\\test.go"
path := "/Users/john/Temp/1/2/3"
path := "/Users/john/Temp/"
_, err := gfsnotify.Add(path, func(event *gfsnotify.Event) {
glog.Println(event)
})
}, true)
// 移除对该path的监听
//gfsnotify.Remove(path)

View File

@ -2,8 +2,10 @@ package main
import (
"fmt"
"gitee.com/johng/gf/g"
"gitee.com/johng/gf/g/os/gspath"
"gitee.com/johng/gf/g/os/gtime"
"time"
)
func main() {
@ -12,8 +14,13 @@ func main() {
rp, err := sp.Add(path)
fmt.Println(err)
fmt.Println(rp)
fmt.Println(gtime.FuncCost(func() {
sp.Search("1")
}))
fmt.Println(sp.Search("1", "index.html"))
gtime.SetInterval(5*time.Second, func() bool {
g.Dump(sp.AllPaths())
return true
})
select {
}
}

View File

@ -1,9 +1,10 @@
package main
import (
"fmt"
"fmt"
"gitee.com/johng/gf/g/os/gfile"
)
func main() {
fmt.Println("123"[2:])
fmt.Println(gfile.TempDir())
}