【源码阅读】osproxy对象存储分布式代理(2)--初始化底层服务和日志器

news/2024/8/26 14:39:57 标签: 分布式, 数据库, golang, gin

文章目录

  • 初始化底层服务
    • 函数返回类型
      • APP
      • Redis
    • newLangGoConfig()函数
    • initLangGoConfig()函数
  • 初始化日志器


  • 上一章【源码阅读】osproxy对象存储分布式代理(1)
  • 下一章

  这部分涉及了对于底层服务的初始化和日志器的初始化两部分

// 初始化底层服务,如数据库
lgConfig := bootstrap.NewConfig("conf/config.yaml")
// 初始化日志器
lgLogger := bootstrap.NewLogger()

初始化底层服务

NewConfig的代码如下

unc NewConfig(confFile string) *config.Configuration {
	// 如果配置已经生效了,就直接沿用配置
	if lgConfig.Conf != nil {
		return lgConfig.Conf
	} else {
		// 初始化配置,包括Configuration结构体和一个同步原语
		lgConfig = newLangGoConfig()
		// 如果用户没有指定配置文件的路径,则使用系统定义的默认路径"conf/config.yaml"
		if confFile == "" {
			lgConfig.initLangGoConfig(confFilePath)
		} else { //否则使用用户给定的配置文件来初始化
			lgConfig.initLangGoConfig(confFile)
		}
		return lgConfig.Conf
	}
}

函数返回类型

  NewConfig函数接收一个字符串类型,返回一个*config.Configuration类型,关于这个类型的定义如下

type Configuration struct {
	App      App                 `mapstructure:"app" json:"app" yaml:"app"`
	Log      Log                 `mapstructure:"log" json:"log" yaml:"log"`
	Database []*plugins.Database `mapstructure:"database" json:"database" yaml:"database"`
	Redis    *plugins.Redis      `mapstructure:"redis" json:"redis" yaml:"redis"`
	Minio    *plugins.Minio      `mapstructure:"minio" json:"minio" yaml:"minio"`
	Cos      *plugins.Cos        `mapstructure:"cos" json:"cos" yaml:"cos"`
	Oss      *plugins.Oss        `mapstructure:"oss" json:"oss" yaml:"oss"`
	Local    *plugins.Local      `mapstructure:"local" json:"local" yaml:"local"`
}

  *config.Configuration内部也包含了许多自定义的结构体,由许多自定义类型,后面的mapstructure 标签需对应 config.yaml 中的配置名称

APP

type App struct {
	Env     string `mapstructure:"env" json:"env" yaml:"env"`
	Port    string `mapstructure:"port" json:"port" yaml:"port"`
	AppName string `mapstructure:"app_name" json:"app_name" yaml:"app_name"`
	AppUrl  string `mapstructure:"app_url" json:"app_url" yaml:"app_url"`
}

  其对应的配置文件config.yaml中对应内容为

app:
  env: prod
  port: 8888                # 服务端口
  app_name: osproxy         # 服务名称
  app_url: http://127.0.0.1

  也就是说,我们首先在App App `mapstructure:"app" json:"app" yaml:"app"` 匹配到app这个配置,获取其中env,port,app_name,app_url等字段,然后复制给结构体APP的变量Env,Port,AppName,AppUrl

Redis

type Redis struct {
	Host     string `mapstructure:"host" json:"host" yaml:"host"`
	Port     string `mapstructure:"port" json:"port" yaml:"port"`
	DB       int    `mapstructure:"db" json:"db" yaml:"db"`
	Password string `mapstructure:"password" json:"password" yaml:"password"`
}
redis:
  host: 127.0.0.1        # 服务地址
  port: 6379             # 服务端口
  db: 0                  # 库选择
  password: 123456       # 密码

  对于Redis也是如此,此处对于其他结构体就不在赘述,总的来说,config.Configuration这个结构体中包括了项目中一些基础服务的配置信息。

newLangGoConfig()函数

func newLangGoConfig() *LangGoConfig {
	return &LangGoConfig{
		Conf: &config.Configuration{},
		Once: &sync.Once{},
	}
}
type LangGoConfig struct {
	Conf *config.Configuration
	Once *sync.Once
}

  newLangGoConfig函数用于初始化Configuration结构体和一个同步原语,同步原语sync.Once只有一个导出的方法,即 Do,该方法接收一个函数参数。在 Do 方法被调用后,该函数将被执行,而且只会执行一次,即使在多个协程同时调用的情况下也是如此,提供一个优雅且并发安全的资源初始化方式。
  sync.Once内部为一个互斥锁,用于对指定函数进行加锁操作,具体源码分析在此

initLangGoConfig()函数

func (lg *LangGoConfig) initLangGoConfig(confFile string) {
	// 基于并发原语对初始化进行并发控制,针对并发场景下的线程安全
	lg.Once.Do(
		func() {
			initConfig(lg.Conf, confFile)
		},
	)
}

  这里就是对并发原语Once的使用,它使用Do的方式使得initconfig函数唯一执行。既确保了配置文件的初始化在多线程的环境下只进行一次,也确保了只有在初始化后,才会调用这些配置信息。

// 根据配置文件初始化底层依赖的配置
func initConfig(conf *config.Configuration, confFile string) {
	pflag.StringVarP(&configPath, "conf", "", filepath.Join(rootPath, confFile),
		"config path, eg: --conf config.yaml")
	if !filepath.IsAbs(configPath) {
		configPath = filepath.Join(rootPath, configPath)
	}
	//lgLogger.Logger.Info("load config:" + configPath)
	fmt.Println("load config:" + configPath)
	v := viper.New()
	v.SetConfigFile(configPath)
	v.SetConfigType("yaml")
	// 使用viper库读取配置文件
	if err := v.ReadInConfig(); err != nil {
		//lgLogger.Logger.Error("read config failed: ", zap.String("err", err.Error()))
		// 读取失败并返回日志信息
		fmt.Println("read config failed: ", zap.String("err", err.Error()))
		panic(err)
	}
	// 解析配置文件
	if err := v.Unmarshal(&conf); err != nil {
		//lgLogger.Logger.Error("config parse failed: ", zap.String("err", err.Error()))
		fmt.Println("config parse failed: ", zap.String("err", err.Error()))
	}
	// 启动对于文件的监控
	v.WatchConfig()
	// 使用viper及时加载文件的变化
	v.OnConfigChange(func(in fsnotify.Event) {
		//lgLogger.Logger.Info("", zap.String("config file changed:", in.Name))
		fmt.Println("", zap.String("config file changed:", in.Name))
		defer func() {
			if err := recover(); err != nil {
				//lgLogger.Logger.Error("config file changed err:", zap.Any("err", err))
				fmt.Println("config file changed err:", zap.Any("err", err))
			}
		}()
		if err := v.Unmarshal(&conf); err != nil {
			//lgLogger.Logger.Error("config parse failed: ", zap.String("err", err.Error()))
			fmt.Println("config parse failed: ", zap.String("err", err.Error()))
		}
	})
	lgConfig.Conf = conf
}

  以上代码便是将配置文件conf.yaml载入系统的过程。值得注意的是作者在这里设置了viper的WatchConfig()函数来实现了配置的热更新。

初始化日志器

NewLogger()的源码如下

func NewLogger() *LangGoLogger {
	if lgLogger.Logger != nil {
		return lgLogger
	} else {
		lgLogger = newLangGoLogger()
		lgLogger.initLangGoLogger(lgConfig.Conf)
		return lgLogger
	}
}

此处定义的结构体LangGoLogger和上面的LangGoConfig差不多,是一个zap库的logger加上一个同步原语。

type LangGoLogger struct {
	Logger *zap.Logger
	Once   *sync.Once
}

接下来的代码也差不多,就是对日志库的logger对象进行初始化,并通过同步原语对其进行控制

// NewLogger 生成新Logger
func NewLogger() *LangGoLogger {
	if lgLogger.Logger != nil {
		return lgLogger
	} else {
		lgLogger = newLangGoLogger()
		lgLogger.initLangGoLogger(lgConfig.Conf)
		return lgLogger
	}
}
// initLangGoLogger 初始化全局log
func (lg *LangGoLogger) initLangGoLogger(conf *config.Configuration) {
	lg.Once.Do(
		func() {
			lg.Logger = initializeLog(conf)
		},
	)
}
func initializeLog(conf *config.Configuration) *zap.Logger {
	// 创建根目录
	createRootDir(conf)

	// 设置日志等级
	setLogLevel(conf)

	if conf.Log.ShowLine {
		options = append(options, zap.AddCaller())
	}

	// 初始化zap
	return zap.New(getZapCore(conf), options...)
}

http://www.niftyadmin.cn/n/5558195.html

相关文章

【Godot4.2】MLTag类:HTML、XML通用标签类

概述 HTML和XML采用类似的标签形式。 之前在Godot中以函数库形式实现了网页标签和内容生成。能用,但是缺点也很明显。函数之间没有从属关系,但是多有依赖,而且没有划分出各种对象和类型。 如果以完全的面向对象形式来设计标签类或者元素类…

vscode 打开远程bug vscode Failed to parse remote port from server output

vscode 打开远程bug vscode Failed to parse remote port from server output 原因如图: 解决:

llama-cpp-python

文章目录 一、关于 llama-cpp-python二、安装安装配置支持的后端Windows 笔记MacOS笔记升级和重新安装 三、高级API1、简单示例2、从 Hugging Face Hub 中提取模型3、聊天完成4、JSON和JSON模式JSON模式JSON Schema 模式 5、函数调用6、多模态模型7、Speculative Decoding8、Em…

webpack生产环境下的配置

css 处理 css提取 下载包 npm i -D mini-css-extract-plugin 配置 module: {rules: [{test: /\.css$/,use: [// style-loader, // 创建style标签,将样式加入js文件MiniCssExtractPlugin.loader, // 提取js中的css成单独的文件css-loader,]}, ]},plugins: [new H…

新书速览|深入理解Hive:从基础到高阶:视频教学版

《深入理解Hive:从基础到高阶:视频教学版》 本书内容 《深入理解Hive:从基础到高阶:视频教学版》采用“理论实战”的形式编写,通过大量的实例,结合作者多年一线开发实战经验,全面地介绍Hive的使用方法。《深入理解Hiv…

Spring与设计模式实战之策略模式

Spring与设计模式实战之策略模式 引言 在现代软件开发中,设计模式是解决常见设计问题的有效工具。它们提供了经过验证的解决方案,帮助开发人员构建灵活、可扩展和可维护的系统。本文将探讨策略模式在Spring框架中的应用,并通过实际例子展示…

单例模式-C#

在C#中实现单例模式,主要目的是确保一个类仅有一个实例,并提供一个全局访问点来获取这个实例。以下是一个简单的单例模式实现示例,它使用了一个私有静态变量来保存类的唯一实例,并提供了一个公有的静态方法来获取这个实例。此外&a…

Telegram Bot、小程序开发(三)Mini Apps小程序

文章目录 一、Telegram Mini Apps小程序二、小程序启动方式三、小程序开发小程序调试模式初始化小程序Keyboard Button Mini Apps 键盘按钮小程序【依赖具体用户信息场景,推荐】**Inline Button Mini Apps内联按钮小程序**initData 的自动传递使用内联菜单时候哪些参数会默认传…