diff --git a/default_config.go b/default_config.go index ed7cb971..af3a0f26 100644 --- a/default_config.go +++ b/default_config.go @@ -22,6 +22,9 @@ env: local http: addr: :8080 disable: false + readTimeout: 2s + writeTimeout: 10s + idleTimeout: 10s grpc: addr: :9090 disable: false diff --git a/otgorm/dependency.go b/otgorm/dependency.go index b5c1888c..7339466e 100644 --- a/otgorm/dependency.go +++ b/otgorm/dependency.go @@ -64,6 +64,14 @@ type databaseConf struct { TablePrefix string `json:"tablePrefix" yaml:"tablePrefix"` SingularTable bool `json:"singularTable" yaml:"singularTable"` } `json:"namingStrategy" yaml:"namingStrategy"` + Pool poolConf `json:"pool" yaml:"pool"` +} + +type poolConf struct { + ConnMaxIdleTime config.Duration `json:"connMaxIdleTime" yaml:"connMaxIdleTime"` + ConnMaxLifeTime config.Duration `json:"connMaxLifeTime" yaml:"connMaxLifeTime"` + MaxIdleConns int `json:"maxIdleConns" yaml:"maxIdleConns"` + MaxOpenConns int `json:"maxOpenConns" yaml:"maxOpenConns"` } type metricsConf struct { @@ -152,7 +160,7 @@ func provideGormConfig(l log.Logger, conf *databaseConf) *gorm.Config { // provideDialector and provideGormConfig. Gorm opens connection to database // while building *gorm.db. This means if the database is not available, the system // will fail when initializing dependencies. -func provideGormDB(dialector gorm.Dialector, config *gorm.Config, tracer opentracing.Tracer) (*gorm.DB, func(), error) { +func provideGormDB(dialector gorm.Dialector, config *gorm.Config, tracer opentracing.Tracer, conf poolConf) (*gorm.DB, func(), error) { db, err := gorm.Open(dialector, config) var nerr *net.OpError @@ -164,6 +172,24 @@ func provideGormDB(dialector gorm.Dialector, config *gorm.Config, tracer opentra if tracer != nil { AddGormCallbacks(db, tracer) } + + sqlDB, err := db.DB() + if err != nil { + return nil, nil, err + } + if !conf.ConnMaxIdleTime.IsZero() { + sqlDB.SetConnMaxIdleTime(conf.ConnMaxIdleTime.Duration) + } + if !conf.ConnMaxLifeTime.IsZero() { + sqlDB.SetConnMaxLifetime(conf.ConnMaxLifeTime.Duration) + } + if conf.MaxIdleConns > 0 { + sqlDB.SetMaxIdleConns(conf.MaxIdleConns) + } + if conf.MaxOpenConns > 0 { + sqlDB.SetMaxOpenConns(conf.MaxOpenConns) + } + return db, func() { if sqlDb, err := db.DB(); err == nil { sqlDb.Close() @@ -202,7 +228,7 @@ func provideDBFactory(options *providersOption) func(p factoryIn) (databaseOut, } gormConfig := provideGormConfig(logger, &conf) options.interceptor(name, gormConfig) - conn, cleanup, err = provideGormDB(dialector, gormConfig, factoryIn.Tracer) + conn, cleanup, err = provideGormDB(dialector, gormConfig, factoryIn.Tracer, conf.Pool) if err != nil { return di.Pair{}, err } @@ -259,6 +285,12 @@ func provideConfig() configOut { TablePrefix string `json:"tablePrefix" yaml:"tablePrefix"` SingularTable bool `json:"singularTable" yaml:"singularTable"` }{}, + Pool: poolConf{ + MaxIdleConns: 1, + MaxOpenConns: 10, + ConnMaxIdleTime: config.Duration{Duration: 5 * time.Minute}, + ConnMaxLifeTime: config.Duration{Duration: 30 * time.Minute}, + }, }, }, "gormMetrics": metricsConf{ diff --git a/otgorm/dependency_test.go b/otgorm/dependency_test.go index d135ed4b..ccdbf6ac 100644 --- a/otgorm/dependency_test.go +++ b/otgorm/dependency_test.go @@ -23,6 +23,12 @@ func TestProvideDBFactory(t *testing.T) { "default": map[string]interface{}{ "database": "sqlite", "dsn": ":memory:", + "pool": map[string]interface{}{ + "maxOpenConns": 10, + "maxIdleConns": 10, + "connMaxIdleTime": "10s", + "connMaxLifetime": "10s", + }, }, "alternative": map[string]interface{}{ "database": "mysql", diff --git a/serve.go b/serve.go index 570c0063..bde168fb 100644 --- a/serve.go +++ b/serve.go @@ -9,8 +9,9 @@ import ( "os/signal" "syscall" + "github.com/DoNewsCode/core/config" "github.com/DoNewsCode/core/contract" - cron "github.com/DoNewsCode/core/cron" + "github.com/DoNewsCode/core/cron" "github.com/DoNewsCode/core/deprecated_cronopts" "github.com/DoNewsCode/core/di" "github.com/DoNewsCode/core/logging" @@ -56,12 +57,31 @@ func (s serveModule) ProvideCommand(command *cobra.Command) { type runGroupFunc func(ctx context.Context, logger logging.LevelLogger) (func() error, func(err error), error) func (s serveIn) httpServe(ctx context.Context, logger logging.LevelLogger) (func() error, func(err error), error) { - if s.Config.Bool("http.disable") { + type httpConfig struct { + Disable bool `json:"disable" yaml:"disable"` + Addr string `json:"addr" yaml:"addr"` + ReadTimeout config.Duration `json:"readTimeout" yaml:"readTimeout"` + ReadHeaderTimeout config.Duration `json:"readHeaderTimeout" yaml:"readHeaderTimeout"` + WriteTimeout config.Duration `json:"writeTimeout" yaml:"writeTimeout"` + IdleTimeout config.Duration `json:"idleTimeout" yaml:"idleTimeout"` + MaxHeaderBytes int `json:"maxHeaderBytes" yaml:"maxHeaderBytes"` + } + + var conf httpConfig + s.Config.Unmarshal("http", &conf) + + if conf.Disable { return nil, nil, nil } if s.HTTPServer == nil { - s.HTTPServer = &http.Server{} + s.HTTPServer = &http.Server{ + ReadTimeout: conf.ReadTimeout.Duration, + ReadHeaderTimeout: conf.ReadHeaderTimeout.Duration, + WriteTimeout: conf.WriteTimeout.Duration, + IdleTimeout: conf.IdleTimeout.Duration, + MaxHeaderBytes: conf.MaxHeaderBytes, + } } router := mux.NewRouter() applyRouter(s.Container, router) @@ -73,8 +93,7 @@ func (s serveIn) httpServe(ctx context.Context, logger logging.LevelLogger) (fun }) s.HTTPServer.Handler = router - - httpAddr := s.Config.String("http.addr") + httpAddr := conf.Addr ln, err := net.Listen("tcp", httpAddr) if err != nil { return nil, nil, errors.Wrap(err, "failed start http server")