golang单例模式加载服务配置

2023-05-16

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}}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

golang单例模式加载服务配置 的相关文章

随机推荐

  • ROS-UART-嵌入式设备之间数据读写

    ROS UART 单片机之间数据读写 ROS通过串口与嵌入式设备进行通信 xff0c 总的来说有两种方式 xff1a 第一种 xff1a 在嵌入式设备加载ROS库 xff0c 让嵌入式设备成为一个节点 xff0c 并发布话题 xff0c 在
  • (五)GPIO标准接口函数

    目录 一 什么是GPIO的标准接口函数二 GPIO标准接口函数三 GPIO口号四 出错的解决方法 xff1a 五 编译内核出错 一 什么是GPIO的标准接口函数 前面访问GPIO的方法 xff1a request mem region 申请
  • Ubuntu20.04安装Navigation功能包

    Ubuntu20 04安装Navigation功能包 1 在用户目录下 xff08 xff09 创建工作空间目录 xff1a nevigation ws src xff1b cd mkdir p nevigation src 2 进入到 n
  • 麻将胡牌算法 极速(速度接近理论极限)

    此麻将胡牌算法优点 xff1a 1 可处理多赖子牌 xff08 万能牌 xff09 2 算法速度极快 xff1a 1ms可大约计算1W 43 副手牌是否可胡 xff08 带赖子 0 08us左右 xff09 xff0c 不带赖子的牌型更快
  • C语言中的输入输出流和缓冲区(重点)详解

    导读 xff1a C语言中我们用到的最频繁的输入输出方式就是scanf 与printf scanf xff1a 从标准输入设备 键盘 读取数据 xff0c 并将值存放在变量中 printf xff1a 将指定的文字 字符串输出到标准输出设备
  • pipenv的基本使用

    1 虚拟环境 虚拟环境是用于依赖项管理和项目隔离的python工具 xff0c 它可以将python程序和pip包管理工具安装在本地的隔离目录中 xff08 非全局安装 xff09 在实际开发中 xff0c 不同项目可能需要的python版
  • 多线程、多进程守护工具

    span class token keyword import span os span class token keyword import span sys span class token keyword import span ti
  • sqlalchemy case when分组查询统计

    1 需求 根据过滤条件将设备按升级状态分组 xff0c 统计总数和各个状态的数量 2 原始数据 3 原生sql语句 span class token keyword select span span class token function
  • 一致性哈希算法

    1 各位看官老爷 xff0c 请移步大佬博客 https www zsythink net archives 1182 2 goalng的脚本测试详见 https xie infoq cn article 78043810ecc807d18
  • git重命名远程分支名称

    例如 xff0c 已经在远程分支的master创建了一个名为feature add device的分支 xff0c 现在想将其更名为hotfix add device 1 重命名远程分支对应的本地分支 span class token fu
  • golang: 密码中允许出现数字、大写字母、小写字母、特殊字符,但至少包含其中2种且长度在8-16之间(四种符号任意满足三种即可)

    要求 密码中允许出现数字 大写字母 小写字母 特殊字符 xff08 64 amp xff09 xff0c 但至少包含其中2种且长度在8 16之间 xff08 四种符号任意满足三种即可 xff09 span class token keywo
  • golang生成分组树状结构

    1 需求 获取分组导航树 2 实现 span class token keyword package span main span class token keyword import span span class token punct
  • 蓝桥杯快速通关篇,pwm方波输出

    pwm方波输出 文章目录 pwm方波输出前言pwm是什么蓝桥桥杯是怎么考pwm输出的具体步骤官方库中的标准例程 修改代码时钟和GPIO输出频率的初始化不同占空比的pwm波输出验证程序是否工作 总结 xff08 重要 xff09 调用 xff
  • python实现文件断点下载

    1 需求 实现文件的断点下载 2 实现 xff1a span class token comment usr bin python span span class token comment encoding 61 utf 8 span s
  • shell脚本移动文件

    需求 移动 iotdata data edge ota file store 下的所有文件到 iotdata data edge download ota目录下 并删除旧目录 shell命令 span class token functio
  • golang字符串列表操作(求包含、交集、并集、差集)

    span class token keyword func span span class token function ContainsString span span class token punctuation span src s
  • golang获取postgres或clickhouse连接

    span class token keyword package span edgedb span class token keyword import span span class token punctuation span span
  • grpc-gateway插件:让客户端通过调http接口来远程调用grpc服务

    背景 xff1a 公司内部各服务 java pyhton go 想调取中台数据中心数据 xff0c 中台有两种服务搭建选择 xff1a 1 REST http请求 2 RPC 远程过程调用 实现及遇到的坑 使用Go将HTTP JSON转编码
  • ubuntu安装和卸载python3.8

    背景 xff1a 工作中 xff0c 可能会分配虚拟机给到开发者 xff0c 开发者需要在上面调试 xff0c 开发 xff0c 打包等操作 xff0c 依赖python环境 操作 网上安装步骤很多 xff0c 但很多文章描述的不够清晰 x
  • golang单例模式加载服务配置

    golang中单例模式体现在很多地方 xff0c 比如init函数 xff0c 当包被导入的时候只会被执行一次 实现单例模式有很多种方式 xff0c 这里给出几种简单的实现 xff1a 互斥锁 对全局共享变量加锁 xff0c 如果该变量不是