Browse Source

enhance: auto restart nodeAnalyticRecord

0xJacky 1 year ago
parent
commit
0265e44c17

+ 1 - 1
frontend/src/version.json

@@ -1 +1 @@
-{"version":"1.9.9","build_id":132,"total_build":202}
+{"version":"1.9.9","build_id":133,"total_build":203}

+ 1 - 1
frontend/version.json

@@ -1 +1 @@
-{"version":"1.9.9","build_id":132,"total_build":202}
+{"version":"1.9.9","build_id":133,"total_build":203}

+ 17 - 9
server/api/environment.go

@@ -1,6 +1,7 @@
 package api
 
 import (
+	"github.com/0xJacky/Nginx-UI/server/internal/analytic"
 	"github.com/0xJacky/Nginx-UI/server/internal/environment"
 	"github.com/0xJacky/Nginx-UI/server/model"
 	"github.com/0xJacky/Nginx-UI/server/query"
@@ -14,13 +15,13 @@ func GetEnvironment(c *gin.Context) {
 
 	envQuery := query.Environment
 
-	environment, err := envQuery.FirstByID(id)
+	env, err := envQuery.FirstByID(id)
 	if err != nil {
 		ErrHandler(c, err)
 		return
 	}
 
-	c.JSON(http.StatusOK, environment)
+	c.JSON(http.StatusOK, env)
 }
 
 func GetEnvironmentList(c *gin.Context) {
@@ -46,7 +47,7 @@ func AddEnvironment(c *gin.Context) {
 		return
 	}
 
-	environment := model.Environment{
+	env := model.Environment{
 		Name:  json.Name,
 		URL:   json.URL,
 		Token: json.Token,
@@ -54,13 +55,15 @@ func AddEnvironment(c *gin.Context) {
 
 	envQuery := query.Environment
 
-	err := envQuery.Create(&environment)
+	err := envQuery.Create(&env)
 	if err != nil {
 		ErrHandler(c, err)
 		return
 	}
 
-	c.JSON(http.StatusOK, environment)
+	go analytic.RestartRetrieveNodesStatus()
+
+	c.JSON(http.StatusOK, env)
 }
 
 func EditEnvironment(c *gin.Context) {
@@ -73,13 +76,13 @@ func EditEnvironment(c *gin.Context) {
 
 	envQuery := query.Environment
 
-	environment, err := envQuery.FirstByID(id)
+	env, err := envQuery.FirstByID(id)
 	if err != nil {
 		ErrHandler(c, err)
 		return
 	}
 
-	_, err = envQuery.Where(envQuery.ID.Eq(environment.ID)).Updates(&model.Environment{
+	_, err = envQuery.Where(envQuery.ID.Eq(env.ID)).Updates(&model.Environment{
 		Name:  json.Name,
 		URL:   json.URL,
 		Token: json.Token,
@@ -90,6 +93,8 @@ func EditEnvironment(c *gin.Context) {
 		return
 	}
 
+	go analytic.RestartRetrieveNodesStatus()
+
 	GetEnvironment(c)
 }
 
@@ -97,15 +102,18 @@ func DeleteEnvironment(c *gin.Context) {
 	id := cast.ToInt(c.Param("id"))
 	envQuery := query.Environment
 
-	environment, err := envQuery.FirstByID(id)
+	env, err := envQuery.FirstByID(id)
 	if err != nil {
 		ErrHandler(c, err)
 		return
 	}
-	err = envQuery.DeleteByID(environment.ID)
+	err = envQuery.DeleteByID(env.ID)
 	if err != nil {
 		ErrHandler(c, err)
 		return
 	}
+
+	go analytic.RestartRetrieveNodesStatus()
+
 	c.JSON(http.StatusNoContent, nil)
 }

+ 2 - 2
server/api/install.go

@@ -1,6 +1,7 @@
 package api
 
 import (
+	"github.com/0xJacky/Nginx-UI/server/internal/boot"
 	"github.com/0xJacky/Nginx-UI/server/model"
 	"github.com/0xJacky/Nginx-UI/server/query"
 	"github.com/0xJacky/Nginx-UI/server/settings"
@@ -56,8 +57,7 @@ func InstallNginxUI(c *gin.Context) {
 	}
 
 	// Init model
-	db := model.Init()
-	query.Init(db)
+	boot.InitDatabase()
 
 	pwd, _ := bcrypt.GenerateFromPassword([]byte(json.Password), bcrypt.DefaultCost)
 

+ 34 - 15
server/internal/analytic/node_record.go

@@ -1,24 +1,33 @@
 package analytic
 
 import (
+	"context"
 	"encoding/json"
 	"github.com/0xJacky/Nginx-UI/server/internal/logger"
 	"github.com/0xJacky/Nginx-UI/server/model"
 	"github.com/0xJacky/Nginx-UI/server/query"
 	"github.com/gorilla/websocket"
-	"github.com/opentracing/opentracing-go/log"
 	"net/http"
 	"time"
 )
 
+var stopNodeRecordChan = make(chan struct{})
+
+func RestartRetrieveNodesStatus() {
+	stopNodeRecordChan <- struct{}{}
+	time.Sleep(10 * time.Second)
+	go RetrieveNodesStatus()
+}
+
 func RetrieveNodesStatus() {
 	NodeMap = make(TNodeMap)
+	errChan := make(chan error)
 
-	env := query.Environment
+	ctx, cancel := context.WithCancel(context.Background())
 
-	if env == nil {
-		return
-	}
+	defer cancel()
+
+	env := query.Environment
 
 	envs, err := env.Find()
 
@@ -27,21 +36,24 @@ func RetrieveNodesStatus() {
 		return
 	}
 
-	errChan := make(chan error)
-
 	for _, v := range envs {
-		go nodeAnalyticLive(v, errChan)
+		go nodeAnalyticLive(v, errChan, ctx)
 	}
 
-	// block at here
-	for err = range errChan {
-		log.Error(err)
+	for {
+		select {
+		case err = <-errChan:
+			logger.Error(err)
+		case <-stopNodeRecordChan:
+			logger.Info("RetrieveNodesStatus exited normally")
+			return // will execute defer cancel()
+		}
 	}
 }
 
-func nodeAnalyticLive(env *model.Environment, errChan chan error) {
+func nodeAnalyticLive(env *model.Environment, errChan chan error, ctx context.Context) {
 	for {
-		err := nodeAnalyticRecord(env)
+		err := nodeAnalyticRecord(env, ctx)
 
 		if err != nil {
 			// set node offline
@@ -58,7 +70,7 @@ func nodeAnalyticLive(env *model.Environment, errChan chan error) {
 	}
 }
 
-func nodeAnalyticRecord(env *model.Environment) (err error) {
+func nodeAnalyticRecord(env *model.Environment, ctx context.Context) (err error) {
 	mutex.Lock()
 	NodeMap[env.ID] = InitNode(env)
 	mutex.Unlock()
@@ -82,9 +94,16 @@ func nodeAnalyticRecord(env *model.Environment) (err error) {
 
 	var nodeStat NodeStat
 
+	go func() {
+		// shutdown
+		<-ctx.Done()
+		_ = c.Close()
+	}()
+
 	for {
 		_, message, err := c.ReadMessage()
-		if err != nil {
+		if err != nil || websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseNoStatusReceived,
+			websocket.CloseNormalClosure) {
 			return err
 		}
 		logger.Debugf("recv: %s %s", env.Name, message)

+ 96 - 0
server/internal/boot/boot.go

@@ -0,0 +1,96 @@
+package boot
+
+import (
+	"github.com/0xJacky/Nginx-UI/server/internal/analytic"
+	"github.com/0xJacky/Nginx-UI/server/internal/cert"
+	"github.com/0xJacky/Nginx-UI/server/internal/logger"
+	"github.com/0xJacky/Nginx-UI/server/model"
+	"github.com/0xJacky/Nginx-UI/server/query"
+	"github.com/0xJacky/Nginx-UI/server/settings"
+	"github.com/go-co-op/gocron"
+	"github.com/google/uuid"
+	"mime"
+	"runtime"
+	"time"
+)
+
+func Kernel() {
+	defer recovery()
+
+	async := []func(){
+		InitJsExtensionType,
+		InitDatabase,
+		InitNodeSecret,
+	}
+
+	syncs := []func(){
+		analytic.RecordServerAnalytic,
+	}
+
+	for _, v := range async {
+		v()
+	}
+
+	for _, v := range syncs {
+		go v()
+	}
+}
+
+func InitAfterDatabase() {
+	syncs := []func(){
+		InitAutoObtainCert,
+		analytic.RetrieveNodesStatus,
+	}
+
+	for _, v := range syncs {
+		go v()
+	}
+}
+
+func recovery() {
+	if err := recover(); err != nil {
+		buf := make([]byte, 1024)
+		runtime.Stack(buf, false)
+		logger.Errorf("%s\n%s", err, buf)
+	}
+}
+
+func InitDatabase() {
+	if "" != settings.ServerSettings.JwtSecret {
+		db := model.Init()
+		query.Init(db)
+
+		InitAfterDatabase()
+	}
+}
+
+func InitNodeSecret() {
+	if "" == settings.ServerSettings.NodeSecret {
+		logger.Warn("NodeSecret is empty, generating...")
+		settings.ServerSettings.NodeSecret = uuid.New().String()
+		settings.ReflectFrom()
+
+		err := settings.Save()
+		if err != nil {
+			logger.Error("Error save settings")
+		}
+		logger.Warn("Generated NodeSecret: ", settings.ServerSettings.NodeSecret)
+	}
+}
+
+func InitJsExtensionType() {
+	// Hack: fix wrong Content Type of .js file on some OS platforms
+	// See https://github.com/golang/go/issues/32350
+	_ = mime.AddExtensionType(".js", "text/javascript; charset=utf-8")
+}
+
+func InitAutoObtainCert() {
+	s := gocron.NewScheduler(time.UTC)
+	job, err := s.Every(30).Minute().SingletonMode().Do(cert.AutoObtain)
+
+	if err != nil {
+		logger.Fatalf("AutoCert Job: %v, Err: %v\n", job, err)
+	}
+
+	s.StartAsync()
+}

+ 1 - 1
server/router/middleware.go

@@ -30,7 +30,7 @@ func recovery() gin.HandlerFunc {
 				buf := make([]byte, 1024)
 				runtime.Stack(buf, false)
 				logger.Errorf("%s\n%s", err, buf)
-				c.JSON(http.StatusInternalServerError, gin.H{
+				c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
 					"message": err.(error).Error(),
 					"error":   errorAction,
 				})

+ 4 - 42
server/server.go

@@ -1,22 +1,13 @@
 package server
 
 import (
-	"github.com/0xJacky/Nginx-UI/server/internal/analytic"
-	"github.com/0xJacky/Nginx-UI/server/internal/cert"
+	"github.com/0xJacky/Nginx-UI/server/internal/boot"
 	"github.com/0xJacky/Nginx-UI/server/internal/logger"
 	"github.com/0xJacky/Nginx-UI/server/internal/nginx"
 	"github.com/0xJacky/Nginx-UI/server/internal/upgrader"
-	"github.com/0xJacky/Nginx-UI/server/model"
-	"github.com/0xJacky/Nginx-UI/server/query"
 	"github.com/0xJacky/Nginx-UI/server/router"
-	"github.com/0xJacky/Nginx-UI/server/settings"
-	"github.com/go-co-op/gocron"
-	"github.com/google/uuid"
 	"github.com/jpillora/overseer"
-	"log"
-	"mime"
 	"net/http"
-	"time"
 )
 
 func GetRuntimeInfo() (r upgrader.RuntimeInfo, err error) {
@@ -25,44 +16,15 @@ func GetRuntimeInfo() (r upgrader.RuntimeInfo, err error) {
 
 func Program(state overseer.State) {
 	defer logger.Sync()
-	// Hack: fix wrong Content Type of .js file on some OS platforms
-	// See https://github.com/golang/go/issues/32350
-	_ = mime.AddExtensionType(".js", "text/javascript; charset=utf-8")
 
 	logger.Info("Nginx config dir path: " + nginx.GetConfPath())
 
-	if "" != settings.ServerSettings.JwtSecret {
-		db := model.Init()
-		query.Init(db)
-	}
-
-	if "" == settings.ServerSettings.NodeSecret {
-		logger.Warn("NodeSecret is empty")
-		settings.ServerSettings.NodeSecret = uuid.New().String()
-		settings.ReflectFrom()
-
-		err := settings.Save()
-		if err != nil {
-			logger.Error("Error save settings")
-		}
-		logger.Warn("Generated NodeSecret: ", settings.ServerSettings.NodeSecret)
-	}
-
-	s := gocron.NewScheduler(time.UTC)
-	job, err := s.Every(30).Minute().SingletonMode().Do(cert.AutoObtain)
+	boot.Kernel()
 
+	err := http.Serve(state.Listener, router.InitRouter())
 	if err != nil {
-		logger.Fatalf("AutoCert Job: %v, Err: %v\n", job, err)
+		logger.Error(err)
 	}
 
-	s.StartAsync()
-
-	go analytic.RecordServerAnalytic()
-	go analytic.RetrieveNodesStatus()
-
-	err = http.Serve(state.Listener, router.InitRouter())
-	if err != nil {
-		log.Fatalln(err)
-	}
 	logger.Info("Server exiting")
 }