Skip to content
Permalink
Browse files

Merge pull request #214 from devfeel/develop

Version 1.7.0 - Add DotWeb.ReSetConfig & NotifyPlugin
  • Loading branch information
devfeel committed Jul 22, 2019
2 parents 62e39c2 + 5b0169d commit 8f1a2fc9207e972f4f0432d81a1073b74aaf7c65
Showing with 181 additions and 7 deletions.
  1. +6 −0 config/configs.go
  2. +1 −1 consts.go
  3. +25 −2 dotweb.go
  4. +17 −3 dotweb_test.go
  5. +90 −0 plugin.go
  6. +33 −1 plugin_test.go
  7. +9 −0 version.MD
@@ -22,6 +22,8 @@ type (
Groups []*GroupNode `xml:"groups>group"`
Middlewares []*MiddlewareNode `xml:"middlewares>middleware"`
ConfigSet core.ReadonlyMap `json:"-" yaml:"-"`
ConfigFilePath string
ConfigType string
}

// AppNode dotweb app global config
@@ -219,6 +221,10 @@ func InitConfig(configFile string, confType ...interface{}) (config *Config, err
// deal config default value
dealConfigDefaultSet(config)

// set config file path
config.ConfigFilePath = realFile
config.ConfigType = cType

return config, nil
}

@@ -3,7 +3,7 @@ package dotweb
// Global define
const (
// Version current version
Version = "1.6.9"
Version = "1.7.0"
)

// Log define
@@ -43,6 +43,7 @@ type (
globalUniqueID string
appLog logger.AppLog
serverStateInfo *core.ServerStateInfo
isRun bool
}

// ExceptionHandle supports exception handling
@@ -312,6 +313,27 @@ func (app *DotWeb) SetConfig(config *config.Config) {
app.Config = config
}

// ReSetConfig reset config for app
// only apply when app is running
// Port can not be modify
// if EnabledPProf, EnabledPProf flag and PProfPort can not be modify
func (app *DotWeb) ReSetConfig(config *config.Config) {
if !app.isRun {
app.Logger().Debug("DotWeb is not running, ReSetConfig can not be call", LogTarget_HttpServer)
return
}

config.Server.Port = app.Config.Server.Port
if app.Config.App.EnabledPProf {
config.App.PProfPort = app.Config.App.PProfPort
config.App.EnabledPProf = app.Config.App.EnabledPProf
}
app.Config = config
app.appLog = logger.NewAppLog()
app.initAppConfig()
app.Logger().Debug("DotWeb ReSetConfig is done.", LogTarget_HttpServer)
}

// StartServer start server with http port
// if config the pprof, will be start pprof server
func (app *DotWeb) StartServer(httpPort int) error {
@@ -378,6 +400,7 @@ func (app *DotWeb) ListenAndServe(addr string) error {
err := app.HttpServer.ListenAndServeTLS(addr, app.HttpServer.ServerConfig().TLSCertFile, app.HttpServer.ServerConfig().TLSKeyFile)
return err
}
app.isRun = true
err := app.HttpServer.ListenAndServe(addr)
return err

@@ -480,14 +503,14 @@ func (app *DotWeb) initRegisterConfigGroup() {
func (app *DotWeb) initPlugins() {
for _, p := range app.pluginMap {
if p.IsValidate() {
go func() {
go func(p Plugin) {
defer func() {
if err := recover(); err != nil {
app.Logger().Error(exception.CatchError("DotWeb::initPlugins run error plugin - "+p.Name(), "", err), LogTarget_HttpServer)
}
}()
p.Run()
}()
}(p)
app.Logger().Debug("DotWeb initPlugins start run plugin - "+p.Name(), LogTarget_HttpServer)
} else {
app.Logger().Debug("DotWeb initPlugins not validate plugin - "+p.Name(), LogTarget_HttpServer)
@@ -1,7 +1,8 @@
package dotweb

import (
"github.com/devfeel/dotweb/framework/file"
"fmt"
"github.com/devfeel/dotweb/config"
"github.com/devfeel/dotweb/test"
"testing"
)
@@ -44,8 +45,21 @@ func Test_IsDevelopmentMode_2(t *testing.T) {
}

func TestDotWeb_UsePlugin(t *testing.T) {
app := New()
app := newConfigDotWeb()
app.UsePlugin(new(testPlugin))
app.UsePlugin(NewNotifyPlugin("test-notify", file.GetCurrentDirectory(), 500))
app.UsePlugin(NewDefaultNotifyPlugin(app))
fmt.Println(app.pluginMap)
app.StartServer(8081)
}

func newConfigDotWeb() *DotWeb {
app := New()
appConfig, err := config.InitConfig("d:/gotmp/dotweb.conf", "xml")
if err != nil {
fmt.Println("dotweb.InitConfig error => " + fmt.Sprint(err))
return nil
}
app.Logger().SetEnabledConsole(true)
app.SetConfig(appConfig)
return app
}
@@ -1,8 +1,98 @@
package dotweb

import (
"fmt"
"github.com/devfeel/dotweb/config"
"os"
"path/filepath"
"time"
)

// Plugin a interface for app's global plugin
type Plugin interface {
Name() string
Run() error
IsValidate() bool
}

// NewDefaultNotifyPlugin return new NotifyPlugin with default config
func NewDefaultNotifyPlugin(app *DotWeb) *NotifyPlugin {
p := new(NotifyPlugin)
p.app = app
p.LoopTime = notifyPlugin_LoopTime
p.Root = app.Config.ConfigFilePath
p.suffix = make(map[string]bool)
p.ModTimes = make(map[string]time.Time)
return p
}

// NewNotifyPlugin return new NotifyPlugin with fileRoot & loopTime & suffix
// if suffix is nil or suffix[0] == "*", will visit all files in fileRoot
/*func NewNotifyPlugin(app *DotWeb, fileRoot string, loopTime int, suffix []string) *NotifyPlugin{
p := new(NotifyPlugin)
p.app = app
p.LoopTime = loopTime
p.Root = fileRoot
Suffix := make(map[string]bool)
if len(suffix) > 0 && suffix[0] != "*" {
for _, v := range suffix {
Suffix[v] = true
}
}
p.suffix = Suffix
p.ModTimes = make(map[string]time.Time)
return p
}*/

const notifyPlugin_LoopTime = 500 //ms

type NotifyPlugin struct {
app *DotWeb
Root string
suffix map[string]bool
LoopTime int
ModTimes map[string]time.Time
}

func (p *NotifyPlugin) Name() string {
return "NotifyPlugin"
}

func (p *NotifyPlugin) IsValidate() bool {
return true
}

func (p *NotifyPlugin) Run() error {
return p.start()
}

func (p *NotifyPlugin) visit(path string, fileinfo os.FileInfo, err error) error {
if err != nil {
return fmt.Errorf("访问文件失败%s", err)
}
ext := filepath.Ext(path)
if !fileinfo.IsDir() && (p.suffix[ext] || len(p.suffix) == 0) {
modTime := fileinfo.ModTime()
if oldModTime, ok := p.ModTimes[path]; !ok {
p.ModTimes[path] = modTime
} else {
if oldModTime.Before(modTime) {
p.app.Logger().Info("NotifyPlugin Reload "+path, LogTarget_HttpServer)
appConfig, err := config.InitConfig(p.app.Config.ConfigFilePath, p.app.Config.ConfigType)
if err != nil {
p.app.Logger().Error("NotifyPlugin Reload "+path+" error => "+fmt.Sprint(err), LogTarget_HttpServer)
}
p.app.ReSetConfig(appConfig)
p.ModTimes[path] = modTime
}
}
}
return nil
}

func (p *NotifyPlugin) start() error {
for {
filepath.Walk(p.Root, p.visit)
time.Sleep(time.Duration(p.LoopTime) * time.Millisecond)
}
}
@@ -1,6 +1,11 @@
package dotweb

import "fmt"
import (
"fmt"
"github.com/devfeel/dotweb/test"
"testing"
"time"
)

type testPlugin struct {
}
@@ -16,3 +21,30 @@ func (p *testPlugin) Run() error {
func (p *testPlugin) IsValidate() bool {
return true
}

func TestNotifyPlugin_Name(t *testing.T) {
app := newConfigDotWeb()
//fmt.Println(app.Config.ConfigFilePath)
p := NewDefaultNotifyPlugin(app)
needShow := "NotifyPlugin"
test.Equal(t, needShow, p.Name())
}

func TestNotifyPlugin_IsValidate(t *testing.T) {
app := newConfigDotWeb()
p := NewDefaultNotifyPlugin(app)
needShow := true
test.Equal(t, needShow, p.IsValidate())
}

func TestNotifyPlugin_Run(t *testing.T) {
app := newConfigDotWeb()
p := NewDefaultNotifyPlugin(app)
go func() {
for {
fmt.Println(p.ModTimes[app.Config.ConfigFilePath])
time.Sleep(time.Duration(600 * time.Millisecond))
}
}()
p.Run()
}
@@ -1,5 +1,14 @@
## dotweb版本记录:

#### Version 1.7.0
* New Feature: 新增NotifyPlugin插件,默认集成监控配置文件变化热重启
* New Feature: 新增DotWeb.ReSetConfig用于运行时重载配置
* About NotifyPlugin:
- 通过NewDefaultNotifyPlugin创建默认集成的NotifyPlugin
- 仅当Dotweb通过配置文件启动方式下有效,监测默认的配置文件
- 当热重启配置文件时,Dotweb本身监听端口以及pprod设置不会重载
- 感谢@地蛋对该插件的支持
* 2019-07-22 14:00 at ShangHai

#### Version 1.6.9
* New Feature: 增加插件机制-Plugin,随App启动一起执行,不会阻塞App启动过程,如需持续运行,在Plugin的Run中自行处理即可。

0 comments on commit 8f1a2fc

Please sign in to comment.
You can’t perform that action at this time.