golang中单例模式体现在很多地方, 比如init函数,当包被导入的时候只会被执行一次。实现单例模式有很多种方式,这里给出几种简单的实现:
互斥锁
对全局共享变量加锁,如果该变量不是nil,那么证明已经被创建了实例,直接返回它的值就好。
type singleton struct {
}
var (
ins *singleton
mu sync.Mutex
)
func Instance() *singleton {
mu.Lock()
defer mu.Unlock()
if ins == nil {
ins = &singleton{
}
}
return ins
}
互斥锁 + 原子操作
加互斥锁是比较耗时的,我们可以使用一个变量来标识是否已经被实例化,维护该变量只需要go中原生支持的原子操作:atomic。
var (
ins *singleton
mu sync.Mutex
created uint32
)
func Instance2() *singleton {
if atomic.LoadUint32(&created) == 1 {
return ins
}
mu.Lock()
defer mu.Unlock()
if ins == nil {
ins = &singleton{
}
atomic.StoreUint32(&created, 1)
}
return ins
}
Once
通过atomic+mutex的方式实现单例模式,其实就是go中Once的实现方式。
var (
ins *singleton
once sync.Once
)
func Instance3() *singleton {
once.Do(func() {
ins = &singleton{
}
})
return ins
}
实战-单例模式加载服务配置文件
.env文件如下
DEBUG=true
POSTGRESQL_SERVER_HOST=172.20.1.24
POSTGRESQL_SERVER_PORT=43007
POSTGRESQL_SERVER_EDGEDB_USER=postgres
POSTGRESQL_SERVER_EDGEDB_PASS=xxxxxxx
POSTGRESQL_SERVER_POOLSIZE=10
RABBITMQ_SERVER_HOST=172.20.1.24
RABBITMQ_SERVER_PORT=43008
RABBITMQ_SERVER_USER=rabbitmq
RABBITMQ_SERVER_PASS=xxxxxx
RABBITMQ_SERVER_VHOST=/
服务里定义了Config结构体,需要从.env文件加载到config实例
package main
import (
"errors"
"fmt"
"github.com/caarlos0/env/v6"
"github.com/go-playground/validator/v10"
"os"
"sync"
"github.com/joho/godotenv"
)
type Config struct {
Postgresql struct {
Host string `yaml:"host" env:"POSTGRESQL_SERVER_HOST" envDefault:"postgresql"`
Port int `yaml:"port" env:"POSTGRESQL_SERVER_PORT" envDefault:"5432"`
EdgedbUser string `yaml:"user" env:"POSTGRESQL_SERVER_EDGEDB_USER" envDefault:"postgres"`
EdgedbPass string `yaml:"pass" env:"POSTGRESQL_SERVER_EDGEDB_PASS" envDefault:"password"`
EdgedbName string `yaml:"name" env:"POSTGRESQL_SERVER_EDGEDB_NAME" envDefault:"edgedb"`
PoolSize int `yaml:"pool_size" env:"POSTGRESQL_SERVER_POOLSIZE" envDefault:"500"` // 设置连接池的最大连接数
}
Rabbitmq struct {
Host string `env:"RABBITMQ_SERVER_HOST" envDefault:"rabbitmq"`
Port int64 `env:"RABBITMQ_SERVER_PORT" envDefault:"5672"`
User string `env:"RABBITMQ_SERVER_USER" envDefault:"rabbitmq"`
Pass string `env:"RABBITMQ_SERVER_PASS" envDefault:"EdgeCore2018"`
Vhost string `env:"RABBITMQ_SERVER_VHOST" envDefault:"/"`
}
Log struct {
DEBUG bool `yanl:"debug" env:"DEBUG"` // 如果配置true, 则ConsoleLevel/FileLevel="debug"
Path string `yaml:"path" env:"LOG_PATH" envDefault:"./logs"`
MaxAge int `yaml:"max_age" env:"LOG_MAX_AGE" envDefault:"30"` // unit: day
MaxSize int `yaml:"max_size" env:"LOG_MAX_SIZE" envDefault:"100"` // unit: M (MillionBytes)
MaxBackups int `yaml:"max_backups" env:"LOG_MAX_BACKUPS" envDefault:"30"` // unit: files number
ConsoleLevel string `yaml:"console_level" env:"LOG_CONSOLE_LEVEL" envDefault:"error"`
ConsoleFormat string `yaml:"console_format" env:"LOG_CONSOLE_FORMAT" envDefault:"str"` //ex: 'str'/'json'
FileLevel string `yaml:"level" env:"LOG_FILE_LEVEL" envDefault:"info"`
FileFormat string `yaml:"file_format" env:"LOG_FILE_FORMAT" envDefault:"json"` //ex: 'str'/'json'
LogPushIsEnabled string `yaml:"log_push_rabbitmq_isenabled" env:"LOG_PUSH_RABBITMQ_ISENABLED" envDefault:"true"`
LogPushLevel string `yaml:"log_push_rabbitmq_level" env:"LOG_PUSH_RABBITMQ_LEVEL" envDefault:"info"`
}
}
var (
cfg *Config
err error
once sync.Once
)
// LoadConfig Load global configs
func LoadConfig(args ...string) (*Config, error) {
// 单例
once.Do(
func() {
var envFile = ".env"
cfg = new(Config)
if len(args) != 0 {
envFile = args[0]
}
// loads .env file to inner ENV
if _, err = os.Stat(envFile); err == nil {
// file exists
if err = godotenv.Load(envFile); err != nil {
return
}
} else if errors.Is(err, os.ErrNotExist) {
// file does not exist, use ENV
} else {
return
}
// loads inner ENV into a Config object (cfg)
if err = env.Parse(cfg); err != nil {
return
}
// check cfg object
if err = validator.New().Struct(cfg); err != nil {
return
}
// Make DEBUG work if setup
if cfg.Log.DEBUG == true {
cfg.Log.ConsoleLevel = "debug"
cfg.Log.FileLevel = "debug"
}
},
)
return cfg, err
}
func main() {
if cfg, err := LoadConfig(); err != nil {
fmt.Println("加载配置失败")
} else {
fmt.Println("加载配置成功, config: ", cfg)
}
}
结果打印:
加载配置成功, config: &{
{
172.20.1.24 43007 postgres xxxxxxx edgedb 10} {
172.20.1.24 43008 rabbitmq xxxxxx /} {
true ./logs 30 100 30 debug str debug json true info}}