Browse Source

Long play mysql connection, optimization

Vova Tkach 6 years ago
parent
commit
110c02a93c
5 changed files with 93 additions and 21 deletions
  1. 3 3
      engine/engine.go
  2. 42 0
      engine/mysqlpool/mysqlpool.go
  3. 30 13
      engine/wrapper/wrapper.go
  4. 18 3
      main.go
  5. 0 2
      modules/modules.go

+ 3 - 3
engine/engine.go

@@ -6,6 +6,7 @@ import (
 	"strings"
 
 	"golang-fave/assets"
+	"golang-fave/engine/mysqlpool"
 	"golang-fave/engine/wrapper"
 	"golang-fave/logger"
 	"golang-fave/modules"
@@ -19,8 +20,8 @@ type Engine struct {
 	Mods *modules.Modules
 }
 
-func Response(l *logger.Logger, m *modules.Modules, w http.ResponseWriter, r *http.Request, s *session.Session, host, port, chost, dirConfig, dirHtdocs, dirLogs, dirTemplate, dirTmp string) bool {
-	wrap := wrapper.New(l, w, r, s, host, port, chost, dirConfig, dirHtdocs, dirLogs, dirTemplate, dirTmp)
+func Response(mp *mysqlpool.MySqlPool, l *logger.Logger, m *modules.Modules, w http.ResponseWriter, r *http.Request, s *session.Session, host, port, chost, dirConfig, dirHtdocs, dirLogs, dirTemplate, dirTmp string) bool {
+	wrap := wrapper.New(l, w, r, s, host, port, chost, dirConfig, dirHtdocs, dirLogs, dirTemplate, dirTmp, mp)
 	eng := &Engine{
 		Wrap: wrap,
 		Mods: m,
@@ -71,7 +72,6 @@ func (this *Engine) Process() bool {
 		utils.SystemErrorPageEngine(this.Wrap.W, err)
 		return true
 	}
-	defer this.Wrap.DB.Close()
 
 	// Show add first user form if no any user in database
 	if !utils.IsFileExists(this.Wrap.DConfig + string(os.PathSeparator) + ".installed") {

+ 42 - 0
engine/mysqlpool/mysqlpool.go

@@ -0,0 +1,42 @@
+package mysqlpool
+
+import (
+	"database/sql"
+	"sync"
+)
+
+type MySqlPool struct {
+	sync.RWMutex
+	connections map[string]*sql.DB
+}
+
+func New() *MySqlPool {
+	r := MySqlPool{}
+	r.connections = map[string]*sql.DB{}
+	return &r
+}
+
+func (this *MySqlPool) Get(key string) *sql.DB {
+	this.Lock()
+	defer this.Unlock()
+	if value, ok := this.connections[key]; ok == true {
+		return value
+	}
+	return nil
+}
+
+func (this *MySqlPool) Set(key string, value *sql.DB) {
+	this.Lock()
+	defer this.Unlock()
+	this.connections[key] = value
+}
+
+func (this *MySqlPool) CloseAll() {
+	this.Lock()
+	defer this.Unlock()
+	for _, c := range this.connections {
+		if c != nil {
+			c.Close()
+		}
+	}
+}

+ 30 - 13
engine/wrapper/wrapper.go

@@ -10,12 +10,13 @@ import (
 	"html/template"
 	"net/http"
 	"os"
+	"time"
 
 	"golang-fave/consts"
+	"golang-fave/engine/mysqlpool"
 	"golang-fave/logger"
 	"golang-fave/utils"
 
-	_ "github.com/go-sql-driver/mysql"
 	"github.com/vladimirok5959/golang-server-sessions/session"
 )
 
@@ -40,12 +41,13 @@ type Wrapper struct {
 	UrlArgs         []string
 	CurrModule      string
 	CurrSubModule   string
+	MSPool          *mysqlpool.MySqlPool
 
 	DB   *sql.DB
 	User *utils.MySql_user
 }
 
-func New(l *logger.Logger, w http.ResponseWriter, r *http.Request, s *session.Session, host, port, chost, dirConfig, dirHtdocs, dirLogs, dirTemplate, dirTmp string) *Wrapper {
+func New(l *logger.Logger, w http.ResponseWriter, r *http.Request, s *session.Session, host, port, chost, dirConfig, dirHtdocs, dirLogs, dirTemplate, dirTmp string, mp *mysqlpool.MySqlPool) *Wrapper {
 	return &Wrapper{
 		l:             l,
 		W:             w,
@@ -62,6 +64,7 @@ func New(l *logger.Logger, w http.ResponseWriter, r *http.Request, s *session.Se
 		UrlArgs:       []string{},
 		CurrModule:    "",
 		CurrSubModule: "",
+		MSPool:        mp,
 	}
 }
 
@@ -73,10 +76,7 @@ func (this *Wrapper) LogError(msg string) {
 	this.l.Log(msg, this.R, true)
 }
 
-func (this *Wrapper) UseDatabase() error {
-	if this.DB != nil {
-		return errors.New("already connected to database")
-	}
+func (this *Wrapper) dbReconnect() error {
 	if !utils.IsMySqlConfigExists(this.DConfig + string(os.PathSeparator) + "mysql.json") {
 		return errors.New("can't read database configuration file")
 	}
@@ -88,17 +88,34 @@ func (this *Wrapper) UseDatabase() error {
 	if err != nil {
 		return err
 	}
+	this.MSPool.Set(this.CurrHost, this.DB)
+	return nil
+}
 
-	// TODO: Make one connection to database
-	// this.DB.SetConnMaxLifetime(time.Second * 5)
-	// this.DB.SetMaxIdleConns(0)
-	// this.DB.SetMaxOpenConns(5)
+func (this *Wrapper) UseDatabase() error {
+	this.DB = this.MSPool.Get(this.CurrHost)
+	if this.DB == nil {
+		if err := this.dbReconnect(); err != nil {
+			return err
+		}
+	}
 
-	err = this.DB.Ping()
-	if err != nil {
+	if err := this.DB.Ping(); err != nil {
 		this.DB.Close()
-		return err
+		if err := this.dbReconnect(); err != nil {
+			return err
+		}
+		if err := this.DB.Ping(); err != nil {
+			this.DB.Close()
+			return err
+		}
 	}
+
+	// Here we are connected
+	this.DB.SetConnMaxLifetime(time.Minute * 30)
+	this.DB.SetMaxIdleConns(2)
+	this.DB.SetMaxOpenConns(2)
+
 	return nil
 }
 

+ 18 - 3
main.go

@@ -10,6 +10,7 @@ import (
 	"golang-fave/assets"
 	"golang-fave/consts"
 	"golang-fave/engine"
+	"golang-fave/engine/mysqlpool"
 	"golang-fave/logger"
 	"golang-fave/modules"
 	"golang-fave/utils"
@@ -86,6 +87,9 @@ func main() {
 	// Init modules
 	mods := modules.New()
 
+	// MySQL connections pool
+	mpool := mysqlpool.New()
+
 	// Init and start web server
 	bootstrap.Start(lg.Handler, fmt.Sprintf("%s:%d", consts.ParamHost, consts.ParamPort), 9, consts.AssetsPath, func(w http.ResponseWriter, r *http.Request, o interface{}) {
 		w.Header().Set("Server", "fave.pro/"+consts.ServerVersion)
@@ -156,16 +160,27 @@ func main() {
 		sess := session.New(w, r, vhost_dir_tmp)
 		defer sess.Close()
 
+		// Convert
+		var mp *mysqlpool.MySqlPool
+		if mpool, ok := o.(*mysqlpool.MySqlPool); ok {
+			mp = mpool
+		}
+
 		// Logic
-		if engine.Response(lg, mods, w, r, sess, host, port, curr_host, vhost_dir_config, vhost_dir_htdocs, vhost_dir_logs, vhost_dir_template, vhost_dir_tmp) {
-			return
+		if mp != nil {
+			if engine.Response(mp, lg, mods, w, r, sess, host, port, curr_host, vhost_dir_config, vhost_dir_htdocs, vhost_dir_logs, vhost_dir_template, vhost_dir_tmp) {
+				return
+			}
 		}
 
 		// Error 404
 		utils.SystemErrorPage404(w)
 	}, func(s *http.Server) {
 		s.SetKeepAlivesEnabled(consts.ParamKeepAlive)
-	}, nil)
+	}, mpool)
+
+	// Close MySQL
+	mpool.CloseAll()
 }
 
 func ServeTemplateFile(w http.ResponseWriter, r *http.Request, file string, path string, dir string) bool {

+ 0 - 2
modules/modules.go

@@ -270,7 +270,6 @@ func (this *Modules) XXXActionFire(wrap *wrapper.Wrapper) bool {
 							wrap.MsgError(err.Error())
 							return true
 						}
-						defer wrap.DB.Close()
 					}
 					if act.Info.WantUser || act.Info.WantAdmin {
 						if !wrap.LoadSessionUser() {
@@ -313,7 +312,6 @@ func (this *Modules) XXXFrontEnd(wrap *wrapper.Wrapper) bool {
 					utils.SystemErrorPageEngine(wrap.W, err)
 					return true
 				}
-				defer wrap.DB.Close()
 			}
 			mod.Front(wrap)
 			return true